次のサンプルオブジェクトがあります。
let foo: Foo = {
'key1': { default: 'foo', fn: (val:string) => val },
'key2': { default: 42, fn: (val:number) => val },
// this should throw an error, because type of default and fn don't match
'key3': { default: true, fn: (val:string) => val }
}
インターフェイスは次のようになります。
interface Foo {
[key: string]: { default: T, fn: (val:T) => any }
}
T
が定義されていないため、これはもちろん機能しません。
だから私はこれをすることを考えました:
interface FooValue<T> {
default: T;
fn: (val:T) => any;
}
interface Foo {
[key: string]: FooValue<?>
}
しかし、そこにも行き詰まりました。 FooValue
のジェネリック型を定義できないからです。
FooValue<any>
を使用すると、もちろんすべてがany
と入力されます。それはうまくいきませんが。
default
のタイプとfn
のパラメータータイプが常に同じであることを確認したいと思います。
何か解決策はありますか?それともできないのですか?
次のように、_Foo<T>
_を マップされたタイプ として定義するのはどうでしょうか。
_interface FooValue<T> {
default: T;
fn: (val: T) => any;
}
type Foo<T> = {
[K in keyof T]: FooValue<T[K]>
}
_
この場合、T
が_{a: string, b: number, c: boolean}
_のような通常のオブジェクトタイプである場合、_Foo<T>
_はそのFoo
に対応したバージョン:_{a: FooValue<string>, b: FooValue<number>, c: FooValue<boolean>
_}です。これで、一部のタイプT
の_Foo<T>
_として推論できる場合にのみ、オブジェクトリテラルを受け入れるヘルパー関数を作成できます。
_function asFoo<T>(foo: Foo<T>): Foo<T> {
return foo;
}
_
TypeScriptコンパイラが マップされたタイプからの推論 を実行できるため、この関数が機能し、T
を_Foo<T>
_から推測できます。これが機能しています:
_let foo = asFoo({
key1: { default: 'foo', fn: (val: string) => val },
key2: { default: 42, fn: (val: number) => val }
});
// inferred as { key1: FooValue<string>; key2: FooValue<number>;}
_
そして、これは失敗しています:
_let badFoo = asFoo(
key1: { default: 'foo', fn: (val: string) => val },
key2: { default: 42, fn: (val: number) => val },
key3: { default: true, fn: (val: string) => val }
});
// error! Types of property 'key3' are incompatible.
// Type 'boolean' is not assignable to type 'string'.
_
お役に立てば幸いです。幸運を!
更新:上記のコードは、_FooValue<string>['fn']
_がany
を返す関数として定義されているため、foo.key1.fn('abc')
が型any
として推論されても問題ないと想定しています。元のオブジェクトリテラルからの出力タイプを忘れてしまいます。プロパティのfoo
メソッドの戻り値の型をfn
にrememberさせたい場合は、これをわずかに異なる方法で行うことができますヘルパー関数:
_function asFoo<T, F>(foo: F & Foo<T>): F {
return foo;
}
let foo = asFoo({
key1: { default: 'foo', fn: (val: string) => val },
key2: { default: 42, fn: (val: number) => val },
// next line would cause error
// key3: { default: true, fn: (val: string)=>val}
})
const key1fnOut = foo.key1.fn('s') // known to be string
const key2fnOut = foo.key2.fn(123) // known to be number
_
そして、それはうまくいきます。この場合、asFoo()
は、一部のT
の入力が_Foo<T>
_であることを確認するだけですが、出力タイプを_Foo<T>
_に強制変換しません。ユースケースによっては、このソリューションを他のソリューションよりも好む場合があります。頑張ってください。