Typescript 如何将类型声明为联合类型的所有可能组合? 太长,读不下去了
我在这里需要的是以某种方式声明一个给定联盟的所有可能组合的联盟Typescript 如何将类型声明为联合类型的所有可能组合? 太长,读不下去了,typescript,combinations,union-types,Typescript,Combinations,Union Types,我在这里需要的是以某种方式声明一个给定联盟的所有可能组合的联盟 type Combinations<SomeUnion, T extends any[]> = /* Some magic */ // ^^^^^^^^^^^^^^^ // this type argument provides the information // about wha
type Combinations<SomeUnion, T extends any[]> = /* Some magic */
// ^^^^^^^^^^^^^^^
// this type argument provides the information
// about what is the length of expected combination.
// then
Combinations<string | number, ['x', 'y']> =
[string, string] |
[string, number] |
[number, string] |
[number, number]
Combinations<string | number | boolean, ['x', 'y']> =
[string, string] |
[string, number] |
[string, boolean] |
[number, string] |
[number, number] |
[number, boolean] |
[boolean, string] |
[boolean, number] |
[boolean, boolean]
Combinations<string | number, ['x', 'y', 'z']> =
[string, string, string] |
[string, string, number] |
[string, number, string] |
[string, number, number] |
[number, string, string] |
[number, string, number] |
[number, number, string] |
[number, number, number]
然而,它变成了假阳性,并抱怨
@我的装饰师
cx:编号{}
这是不正确的
这一次的原因是x:number=>void与arg_0:Defined=>void不兼容。数字是定义的缩小版本,缩小参数类型会导致LSP,从而导致错误
问题是:
FixedLengthFunction解析为…args:[已定义,已定义]=>void,进一步解析为arg\u 0:Defined,arg\u 1:Defined=>void
而我真正想要的是:
(...args:
[number, number] |
[string, number] |
[boolean, string]
/* ...and all other possible combinations of length 2 */
) => void
所以,我需要的是这篇文章顶部的神奇类型组合。生成这样的组合是个坏主意。它将在编译过程中失控并产生性能问题。您可能可以使用递归类型别名来执行此操作,但这是非常不鼓励的,即,您可以欺骗编译器执行此操作,但将来可能无法使用 尽管如此,我认为你指出的问题是错误的。你说参数少的函数可以分配给参数多的函数。事实并非如此。通常情况下,typescript允许在需要参数较多的函数时,分配参数较少的函数。函数实现将忽略额外参数,不会造成任何伤害:
let fn: (a: number) => void = function () { console.log("Don't care about your args!"); }
fn(1)// 1 is ignored but that is ok
我们可以根据元组具有长度属性这一事实以及我们可以推断类的实际类型并从类型中提取实际参数这一事实,强制参数数量严格相等:
type FixedLengthFunction<T extends any[]> = (...args: { [k in keyof T]: any }) => void
type ErrorIfDifferentLength<TMethod, TExpected extends any[]> =
TMethod extends (...a: infer TParams) => any ?
TParams['length'] extends TExpected['length'] ? {}: { "!Error": "Number of parameters differ:", actual: TParams['length'], expected: TExpected['length'] } : {}
function myDecorator<T extends any[]>(...a: T) {
return <K extends string, TClass extends Record<K, FixedLengthFunction<T>>>(target: TClass & ErrorIfDifferentLength<TClass[K], T>, key: K): void => {
}
}
// Note: WAI => Works as intented
class Foo {
@myDecorator()
a() {}
// expected to be correct,
// and actually passes the type system.
// WAI
@myDecorator()
b(x: number) {}
// expected to be incorrect since 'b' has one more argument,
// and actually catched by the type system.
// WAI
@myDecorator('m')
c(x: number) {}
// expected to be correct,
// and actually passes the type system.
// WAI
@myDecorator('m')
d() {}
// Argument of type 'Foo' is not assignable to parameter of type 'Foo & { "!Error": "Number of parameters differ:"; method: "d"; actual: 0; expected: 1; }'.
// WAI
}
杰出的您是一个救世主。实际上,通过ErrorIfDifferentLength确保类型安全,我们可以摆脱FixedLength功能,并在TClass扩展记录时约束TClass