带有有序数组/元组的 Typescript 泛型
Typescript Generics with ordered array / tuples
我有一个函数,它有两个参数:
- 对象键列表
- 一个只有一个参数的函数:对象键的值
没关系,如果内部函数以数组形式获取值:
myFunction<T>(params: (keyof T)[], innerFn: (T[keyof T][]) => any)
或作为对象:
myFunction<T>(params: (keyof T)[], innerFn: ({[key in keyof T]: T[key]}) => any)
只要打字正常;-)
这两个函数签名当然没有按预期工作。
以下是我想如何使用它们的一些示例:
interface MyInterface {
foo: string;
bar: number;
baz: boolean[];
}
myFunction<MyInterface>(
['foo'],
([foo]) => null // foo should be typed as 'string'
);
myFunction<MyInterface>(
['abc'], // should throw error, because there's no abc key in MyInterface
([abc]) => null
);
myFunction<MyInterface>(
['bar', 'baz'],
([bar, baz]) => null // bar should be typed as 'number', baz as 'boolean[]'
);
函数签名应该:
- 根据提供的
MyInterface
限制params
数组
- 根据
params
数组正确输入 innerFn
这可以使用 TypeScript 吗?
如果yes
:如何实现?
你可以做一些你想做的事情,但有一些注意事项:
你必须将你的函数分解成几个:
泛型类型参数 T
需要由调用者显式指定,但函数参数也需要是泛型的(例如 A extends keyof T
)。您不能使用带有 <T, A, B>
等参数的函数,其中 T
需要手动指定,但 A
和 B
需要从参数中推断出来。您基本上得到 all or nothing from type parameter inference. One way to work around this is to use a curried function 其中 myFunction<T>()
returns 另一个函数 <A, B>
作为类型参数。也就是这样改:
// F<> is just a placeholder
declare myFunction<T, A, B>(args: [A, B], funcs: F<T,A,B>): void;
// specify everything, annoying
myFunction<MyInterface, 'foo', 'bar'>(['foo', 'bar'], ([foo, bar])=>null);
// specify nothing, doesn't work
myFunction(['foo', 'bar'], ([foo, bar])=>null); // error
到
myFunction<T>(): <A, B>(args: [A, B], funcs: F<T,A,B>) => void
myFunction<MyInterface>()(['foo','bar'], ([foo, bar])=>null); // works
您可以看到它如何允许您在 myFunction<MyInterface>()
中指定 MyInterface
并允许编译器在后续调用中推断 A
和 B
。
tuples很难通用:
TypeScript 缺少 variadic kinds, so it's not really possible to give a single signature to many functions which act on "tuples of any length". You end up needing to work around this by providing overloaded 作用于您关心的特定长度的元组的函数签名。 T
键元组到 T
相应值元组的“简单”映射,您希望将其表示为
declare myFunction<T,K extends keyof T>(
keys: [...K], funcs: ([...T[K]]) => void): void;
最终扩展到
// one-tuple
declare myFunction<T, A extends keyof T>(
keys: [A], funcs: ([T[A]) => void): void;
// two-tuple
declare myFunction<T, A extends keyof T, B extends keyof T>(
keys: [A,B], funcs: ([T[A],T[B]) => void): void;
// three-tuple
declare myFunction<T, A extends keyof T, B extends keyof T, C extends keyof T>(
keys: [A,B,C], funcs: ([T[A],T[B],T[C]) => void): void;
// et cetera
只要你能站得住。
放在一起:
那么,让我们像这样咖喱和重载:
declare function myFunction<T>(): {
<A extends keyof T, B extends keyof T, C extends keyof T, D extends keyof T>(
a: [A, B, C, D], f: (a: [T[A], T[B], T[C], T[D]]) => void
): void;
<A extends keyof T, B extends keyof T, C extends keyof T>(
a: [A, B, C], f: (a: [T[A], T[B], T[C]]) => void
): void;
<A extends keyof T, B extends keyof T>(
a: [A, B], f: (a: [T[A], T[B]]) => void
): void;
<A extends keyof T>(
a: [A], f: (a: [T[A]]) => void
): void;
}
处理最大长度为 4 的元组。如果您愿意,可以随意添加更多重载。让我们看看它是否有效:
myFunction<MyInterface>()(
['foo'],
([foo]) => null // foo is a string
);
myFunction<MyInterface>()(
['abc'], // error, '"abc"' is not '"foo" | "bar" | "baz"'.
([abc]) => null // error, abc is implicitly any
);
myFunction<MyInterface>()(
['bar', 'baz'],
([bar, baz]) => null // bar is number, baz is boolean[]
);
看起来不错!希望有所帮助;祝你好运。
我有一个函数,它有两个参数:
- 对象键列表
- 一个只有一个参数的函数:对象键的值
没关系,如果内部函数以数组形式获取值:
myFunction<T>(params: (keyof T)[], innerFn: (T[keyof T][]) => any)
或作为对象:
myFunction<T>(params: (keyof T)[], innerFn: ({[key in keyof T]: T[key]}) => any)
只要打字正常;-)
这两个函数签名当然没有按预期工作。
以下是我想如何使用它们的一些示例:
interface MyInterface {
foo: string;
bar: number;
baz: boolean[];
}
myFunction<MyInterface>(
['foo'],
([foo]) => null // foo should be typed as 'string'
);
myFunction<MyInterface>(
['abc'], // should throw error, because there's no abc key in MyInterface
([abc]) => null
);
myFunction<MyInterface>(
['bar', 'baz'],
([bar, baz]) => null // bar should be typed as 'number', baz as 'boolean[]'
);
函数签名应该:
- 根据提供的
MyInterface
限制params
数组 - 根据
params
数组正确输入innerFn
这可以使用 TypeScript 吗?
如果yes
:如何实现?
你可以做一些你想做的事情,但有一些注意事项:
你必须将你的函数分解成几个:
泛型类型参数 T
需要由调用者显式指定,但函数参数也需要是泛型的(例如 A extends keyof T
)。您不能使用带有 <T, A, B>
等参数的函数,其中 T
需要手动指定,但 A
和 B
需要从参数中推断出来。您基本上得到 all or nothing from type parameter inference. One way to work around this is to use a curried function 其中 myFunction<T>()
returns 另一个函数 <A, B>
作为类型参数。也就是这样改:
// F<> is just a placeholder
declare myFunction<T, A, B>(args: [A, B], funcs: F<T,A,B>): void;
// specify everything, annoying
myFunction<MyInterface, 'foo', 'bar'>(['foo', 'bar'], ([foo, bar])=>null);
// specify nothing, doesn't work
myFunction(['foo', 'bar'], ([foo, bar])=>null); // error
到
myFunction<T>(): <A, B>(args: [A, B], funcs: F<T,A,B>) => void
myFunction<MyInterface>()(['foo','bar'], ([foo, bar])=>null); // works
您可以看到它如何允许您在 myFunction<MyInterface>()
中指定 MyInterface
并允许编译器在后续调用中推断 A
和 B
。
tuples很难通用:
TypeScript 缺少 variadic kinds, so it's not really possible to give a single signature to many functions which act on "tuples of any length". You end up needing to work around this by providing overloaded 作用于您关心的特定长度的元组的函数签名。 T
键元组到 T
相应值元组的“简单”映射,您希望将其表示为
declare myFunction<T,K extends keyof T>(
keys: [...K], funcs: ([...T[K]]) => void): void;
最终扩展到
// one-tuple
declare myFunction<T, A extends keyof T>(
keys: [A], funcs: ([T[A]) => void): void;
// two-tuple
declare myFunction<T, A extends keyof T, B extends keyof T>(
keys: [A,B], funcs: ([T[A],T[B]) => void): void;
// three-tuple
declare myFunction<T, A extends keyof T, B extends keyof T, C extends keyof T>(
keys: [A,B,C], funcs: ([T[A],T[B],T[C]) => void): void;
// et cetera
只要你能站得住。
放在一起:
那么,让我们像这样咖喱和重载:
declare function myFunction<T>(): {
<A extends keyof T, B extends keyof T, C extends keyof T, D extends keyof T>(
a: [A, B, C, D], f: (a: [T[A], T[B], T[C], T[D]]) => void
): void;
<A extends keyof T, B extends keyof T, C extends keyof T>(
a: [A, B, C], f: (a: [T[A], T[B], T[C]]) => void
): void;
<A extends keyof T, B extends keyof T>(
a: [A, B], f: (a: [T[A], T[B]]) => void
): void;
<A extends keyof T>(
a: [A], f: (a: [T[A]]) => void
): void;
}
处理最大长度为 4 的元组。如果您愿意,可以随意添加更多重载。让我们看看它是否有效:
myFunction<MyInterface>()(
['foo'],
([foo]) => null // foo is a string
);
myFunction<MyInterface>()(
['abc'], // error, '"abc"' is not '"foo" | "bar" | "baz"'.
([abc]) => null // error, abc is implicitly any
);
myFunction<MyInterface>()(
['bar', 'baz'],
([bar, baz]) => null // bar is number, baz is boolean[]
);
看起来不错!希望有所帮助;祝你好运。