如何定义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
-ized 版本:{a: FooValue<string>, b: FooValue<number>, c: FooValue<boolean>
}。Foo<T>
现在,您可以创建一个辅助函数,该函数仅在可以推断为某种类型的对象文字时才接受对象文字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'.
希望有帮助。祝你好运!
更新:上面的代码假设你可以foo.key1.fn('abc')
被推断为 type any
,因为FooValue<string>['fn']
它被定义为返回的函数any
。它有点忘记了原始对象文字的输出类型。如果你想foo
记住它的属性fn
方法的返回类型,你可以做这个稍微不同的辅助函数:
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()
只需验证输入是否为 a Foo<T>
,T
但不会将输出类型强制为 a Foo<T>
。根据您的用例,您可能更喜欢此解决方案而不是其他解决方案。再次祝你好运。