如何将受约束的字符串传递给使用相同约束的参数作为参数的类型
How to pass a constrained string to a type using as parameters a parameter using that same constraint
我可以建立这样的数字:
type Digits = '0'| '1'| '2'| '3'| '4'| '5'| '6'| '7'| '8'| '9'
type NonZero = Exclude <Digits, '0'>
type Negative = '-'
type PositiveNumbers =
| NonZero
| `${NonZero}${Digits}`
| `${NonZero}${Digits}${Digits}`
| `${NonZero}${Digits}${Digits}${Digits}`
| `${NonZero}${Digits}${Digits}${Digits}${Digits}`
| `${NonZero}${Digits}${Digits}${Digits}${Digits}${Digits}` // Error: Expression produces a union type that is too complex to represent.(2590)
type Numbers =
| PositiveNumbers
| `${Negative}${PositiveNumbers}`
但是我只能使用 -99999
和 99999
中的数字,除此之外,在其他类型中使用类型 Numbers
会使编译器陷入困境。所以我使用一种类型来表示数字作为约束,如下所示:
type ConstrainNumber <N extends string> =
N extends '0'
? unknown
: N extends `${infer Char}${infer Rest}`
? Char extends '0'
? never
: Char extends Negative
? ConstrainNumber <Rest>
: ConstrainNumberRec <N, Exclude <Digits, '0'>>
: never
type ConstrainNumberRec <S extends string, D = Digits> =
S extends `${infer Char}${infer Rest}`
? Char extends D ? ConstrainNumberRec <Rest> : never
: unknown
在类型上使用它时,它按预期工作:
type UseNumbers <N extends string & _N, _N = ConstrainNumber <N>> = N
type testValid = UseNumbers <'2875467'>
type testInvalid = UseNumbers <'02875467'> // Expected error: Type 'string' does not satisfy the constraint 'never'.(2344)
但是当我使用使用 UseNumbers 的类型时它会中断
type UseUseNumbers <N extends string & _N, _N = ConstrainNumber <N>> =
UseNumbers <N> // Unwanted Error: type 'N' does not satisfy the constraint 'string & ConstrainNumber<N>'
我如何将约束字符串传递给使用相同约束的参数作为参数的类型?
而且,我不确定猜它为什么会坏?在 UseNumbers <N>
N
上是否已经有约束?
主要问题是 generic parameter defaults are not generic constraints:
type Foo<T extends number> = T;
type BadFoo = Foo<string>; // error, not satisfying constraint
type Bar<T = number> = T;
type DefaultBar = Bar; // number
type StillGoodBar = Bar<string>; // string
上面Foo
中的类型参数T
是constrained到number
,而在Bar
中是不受约束,但只是 默认 到 number
。所以不能写Foo<string>
。如果你只写 Bar
它将是 Bar<number>
,但是没有什么能阻止某人写 Bar<string>
。这不是错误,编译器无法在 Bar
内部假设 T
将是 number
.
的子类型
在这个定义中,
type UseUseNumbers<N extends string & _N, _N = ConstrainNumber<N>> = ...
_N
类型参数不受约束。它可以是任何东西,即使它 默认 到 ConstrainNumber<N>
。只有 N
被限制为 string & _N
.
这里的主要用例是没有人指定 _N
,并且 UseUseNumbers<N>
等效于 UseUseNumbers<N, ConstrainNumber<N>>
,当且仅当 N extends string & ConstrainNumber<N>
时才编译。这是避免如果您编写 type Oopsie<N extends string & ConstrainNumber<N>>
会发生的循环约束错误的巧妙方法,但该技巧只有在 _N
不依赖于 N
.[= 的可能性保持开放的情况下才有效。 62=]
因此编译器无法知道 _N
是什么特别的东西。一些流氓开发人员可能会编写 UseUseNumbers<string, unknown>
或 UseUseNumbers<"abc","abc" | "def">
或其他一些疯狂的 off-label 使用 UseUseNumbers
碰巧编译。这可能是不可避免的(尽管问题的可避免性或缺乏是 off-topic)但也不太可能实际发生,特别是如果您记录 _N
仅供“内部使用”(但同样,off-topic).您要考虑多少这种可能性取决于您。
无论如何,编译器对
感到不满
type UseUseNumbers<N extends string & _N, _N = ConstrainNumber<N>> =
UseNumbers<N> // <-- error
为什么?因为 UseNumbers<N>
的计算结果为 UseNumbers<N, ConstrainNumber<N>>
,它(类似于 UseUseNumbers
)仅在 N extends string & ConstrainNumber<N>
时才编译。但是编译器并不知道当前的N
,它只知道扩展string & _N
,而_N
是不受约束的。所以它不编译。
解决这个问题的方法是明确假设没有人会在 UseUseNumbers
中弄乱 _N
,并将其传递给 UseNumbers
:
type UseUseNumbers<N extends string & _N, _N = ConstrainNumber<N>> =
UseNumbers<N, _N> // <-- this fixes it
由于UseNumbers
中的_N
是无约束的,所以可以写成UseNumbers<N, _N>
,当且仅当N extends string & _N
时编译通过。但是我们知道 UseUseNumbers
中 N
的定义是正确的。只要人们写 UseUseNumbers<N>
并且不传递第二个类型的参数,那么这将根据需要评估为 UseNumbers<N>
。
我可以建立这样的数字:
type Digits = '0'| '1'| '2'| '3'| '4'| '5'| '6'| '7'| '8'| '9'
type NonZero = Exclude <Digits, '0'>
type Negative = '-'
type PositiveNumbers =
| NonZero
| `${NonZero}${Digits}`
| `${NonZero}${Digits}${Digits}`
| `${NonZero}${Digits}${Digits}${Digits}`
| `${NonZero}${Digits}${Digits}${Digits}${Digits}`
| `${NonZero}${Digits}${Digits}${Digits}${Digits}${Digits}` // Error: Expression produces a union type that is too complex to represent.(2590)
type Numbers =
| PositiveNumbers
| `${Negative}${PositiveNumbers}`
但是我只能使用 -99999
和 99999
中的数字,除此之外,在其他类型中使用类型 Numbers
会使编译器陷入困境。所以我使用一种类型来表示数字作为约束,如下所示:
type ConstrainNumber <N extends string> =
N extends '0'
? unknown
: N extends `${infer Char}${infer Rest}`
? Char extends '0'
? never
: Char extends Negative
? ConstrainNumber <Rest>
: ConstrainNumberRec <N, Exclude <Digits, '0'>>
: never
type ConstrainNumberRec <S extends string, D = Digits> =
S extends `${infer Char}${infer Rest}`
? Char extends D ? ConstrainNumberRec <Rest> : never
: unknown
在类型上使用它时,它按预期工作:
type UseNumbers <N extends string & _N, _N = ConstrainNumber <N>> = N
type testValid = UseNumbers <'2875467'>
type testInvalid = UseNumbers <'02875467'> // Expected error: Type 'string' does not satisfy the constraint 'never'.(2344)
但是当我使用使用 UseNumbers 的类型时它会中断
type UseUseNumbers <N extends string & _N, _N = ConstrainNumber <N>> =
UseNumbers <N> // Unwanted Error: type 'N' does not satisfy the constraint 'string & ConstrainNumber<N>'
我如何将约束字符串传递给使用相同约束的参数作为参数的类型?
而且,我不确定猜它为什么会坏?在 UseNumbers <N>
N
上是否已经有约束?
主要问题是 generic parameter defaults are not generic constraints:
type Foo<T extends number> = T;
type BadFoo = Foo<string>; // error, not satisfying constraint
type Bar<T = number> = T;
type DefaultBar = Bar; // number
type StillGoodBar = Bar<string>; // string
上面Foo
中的类型参数T
是constrained到number
,而在Bar
中是不受约束,但只是 默认 到 number
。所以不能写Foo<string>
。如果你只写 Bar
它将是 Bar<number>
,但是没有什么能阻止某人写 Bar<string>
。这不是错误,编译器无法在 Bar
内部假设 T
将是 number
.
在这个定义中,
type UseUseNumbers<N extends string & _N, _N = ConstrainNumber<N>> = ...
_N
类型参数不受约束。它可以是任何东西,即使它 默认 到 ConstrainNumber<N>
。只有 N
被限制为 string & _N
.
这里的主要用例是没有人指定 _N
,并且 UseUseNumbers<N>
等效于 UseUseNumbers<N, ConstrainNumber<N>>
,当且仅当 N extends string & ConstrainNumber<N>
时才编译。这是避免如果您编写 type Oopsie<N extends string & ConstrainNumber<N>>
会发生的循环约束错误的巧妙方法,但该技巧只有在 _N
不依赖于 N
.[= 的可能性保持开放的情况下才有效。 62=]
因此编译器无法知道 _N
是什么特别的东西。一些流氓开发人员可能会编写 UseUseNumbers<string, unknown>
或 UseUseNumbers<"abc","abc" | "def">
或其他一些疯狂的 off-label 使用 UseUseNumbers
碰巧编译。这可能是不可避免的(尽管问题的可避免性或缺乏是 off-topic)但也不太可能实际发生,特别是如果您记录 _N
仅供“内部使用”(但同样,off-topic).您要考虑多少这种可能性取决于您。
无论如何,编译器对
感到不满type UseUseNumbers<N extends string & _N, _N = ConstrainNumber<N>> =
UseNumbers<N> // <-- error
为什么?因为 UseNumbers<N>
的计算结果为 UseNumbers<N, ConstrainNumber<N>>
,它(类似于 UseUseNumbers
)仅在 N extends string & ConstrainNumber<N>
时才编译。但是编译器并不知道当前的N
,它只知道扩展string & _N
,而_N
是不受约束的。所以它不编译。
解决这个问题的方法是明确假设没有人会在 UseUseNumbers
中弄乱 _N
,并将其传递给 UseNumbers
:
type UseUseNumbers<N extends string & _N, _N = ConstrainNumber<N>> =
UseNumbers<N, _N> // <-- this fixes it
由于UseNumbers
中的_N
是无约束的,所以可以写成UseNumbers<N, _N>
,当且仅当N extends string & _N
时编译通过。但是我们知道 UseUseNumbers
中 N
的定义是正确的。只要人们写 UseUseNumbers<N>
并且不传递第二个类型的参数,那么这将根据需要评估为 UseNumbers<N>
。