[英]Typescript Syntax for Type Inference based on kind
I have an issue writing proper typescript syntax for strict infering where:我在编写正确的打字稿语法以进行严格推断时遇到问题:
type KindA = {kind:'a'};
type KindB = {kind:'b'};
type KindC = {kind:'c'};
type AllKinds = KindA | KindB | KindC;
function create<T extends AllKinds>(kind:AllKinds['kind']):T {
switch(kind) {
case "a": return {kind:'a'};
case "b": return {kind:'b'};
case "c": return {kind:'c'};
}
}
create("a");
I wonder if this is possible with the latest Typescript.我想知道最新的 Typescript 是否可以实现。
With my other approach (ie case "a": return {kind:'b'} as T;
) the returned value is not type checked to what I need.使用我的另一种方法(即
case "a": return {kind:'b'} as T;
),返回的值不会根据我的需要进行类型检查。
It is unsafe to return T
in your case.在您的情况下返回
T
是不安全的。
See why:看看为什么:
type KindA = { kind: 'a' };
type KindB = { kind: 'b' };
type KindC = { kind: 'c' };
type AllKinds = KindA | KindB | KindC;
function create<T extends AllKinds>(kind: AllKinds['kind']): T {
switch (kind) {
case "a": return { kind: 'a' };
case "b": return { kind: 'b' };
case "c": return { kind: 'c' };
}
}
const result = create<{ kind: 'a', WAAAT: () => {} }>("a")
result.WAAAT() // compiler but causes an error in runtime
Generic argument should in 90% of cases depend on input value.通用参数应该在 90% 的情况下取决于输入值。
See this example:看这个例子:
type KindA = { kind: 'a' };
type KindB = { kind: 'b' };
type KindC = { kind: 'c' };
type AllKinds = KindA | KindB | KindC;
const builder = <Kind extends AllKinds['kind']>(kind: Kind) => ({ kind })
const result = builder("a"); // {kind: 'a' }
See this answer, this answer and my article for more context有关更多上下文,请参阅此答案、 此答案和我的文章
Do I get it right that witch current Typescript, one can not have both ?
我是否正确地认为女巫当前的 Typescript,一个不能同时拥有?
The problem is not in switch
statement but rather in explicit return type.问题不在于
switch
语句,而在于显式返回类型。 Return type can't depend on a generic value which is not binded with function arguments.返回类型不能依赖于未与函数参数绑定的泛型值。
In fact, it is possible to achieve waht you want:事实上,有可能实现你想要的:
type KindA = { kind: 'a' };
type KindB = { kind: 'b' };
type KindC = { kind: 'c' };
type AllKinds = KindA | KindB | KindC;
function create<Kind extends AllKinds['kind']>(kind: Kind): Extract<AllKinds, { kind: Kind }>
function create(kind: AllKinds['kind']) {
switch (kind) {
case "a": return { kind: 'a' };
case "b": return { kind: 'b' };
case "c": return { kind: 'c' };
}
}
const result1 = create("a") // KindA
const result2 = create("b") // KindB
const result3 = create("c") // KindC
As you might have noticed, I have used function overloading .您可能已经注意到,我使用了函数重载。 It makes TS compiler less strict.
它使 TS 编译器不那么严格。 In other words, provides a bit of unsafety but in the same time makes it more readable and infers return type.
换句话说,提供了一些不安全性,但同时使其更具可读性并推断返回类型。
AFAIK, function overloads behaves bivariantly . AFAIK,函数重载的行为是双变的。 Hence, it is up to you which option is better
因此,由您决定哪个选项更好
The error is quite descriptive.该错误是非常具有描述性的。
Type '{ kind: "a"; }' is not assignable to type 'T'.
'{ kind: "a"; }' is assignable to the constraint of type 'T',
but 'T' could be instantiated with a different subtype of constraint 'AllKinds'.
This problem occurs even when you use a simpler type constraint.即使您使用更简单的类型约束,也会出现此问题。
function foo<T extends string>(): T {
return 'foo';
}
Here we'd get the following error.在这里我们会得到以下错误。
Type 'string' is not assignable to type 'T'.
'string' is assignable to the constraint of type 'T',
but 'T' could be instantiated with a different subtype of constraint 'string'.
The problem is that we said that we'd return something of type T
, but T
is not the same type as string
.问题是我们说过我们会返回
T
类型的东西,但T
与string
类型不同。 Yes, the type T
extends string
but all that means is that T
is a subtype of string
.是的,类型
T
扩展了string
但这意味着T
是string
的子类型。 For example, the type 'bar'
is a subtype of string
.例如,类型
'bar'
是string
的子类型。 Hence, we can instantiate T
with 'bar'
.因此,我们可以用
'bar'
实例化T
Hence, we'd expect the return value to be 'bar'
but the return value is 'foo'
.因此,我们希望返回值是
'bar'
但返回值是'foo'
。
The solution is to simply not use generics.解决方案是简单地不使用泛型。 If you want to return a
string
then just say that you're returning a string
.如果你想返回一个
string
那么只需说你正在返回一个string
。 Don't say that you're returning a value of some subtype T
of string
.不要说您要返回
string
的某个子类型T
的值。
function foo(): string {
return 'foo';
}
Similarly, if you want to return a value of type AllKinds
then just say that you're returning a value of type AllKinds
.同样,如果您想返回
AllKinds
类型的值,那么只需说您正在返回AllKinds
类型的值。 Don't say that you're returning a value of some subtype T
of AllKinds
.不要说您要返回
AllKinds
的某个子类型T
的AllKinds
。
type KindA = {kind:'a'};
type KindB = {kind:'b'};
type KindC = {kind:'c'};
type AllKinds = KindA | KindB | KindC;
function create(kind:AllKinds['kind']): AllKinds {
switch(kind) {
case "a": return {kind:'a'};
case "b": return {kind:'b'};
case "c": return {kind:'c'};
}
}
create("a");
Edit: You need dependent types to do what you want.编辑:你需要依赖类型来做你想做的事。 TypeScript doesn't have dependent types.
TypeScript 没有依赖类型。 However, you can create a custom fold function that provides additional type safety.
但是,您可以创建提供额外类型安全性的自定义折叠函数。
type Kind = 'a' | 'b' | 'c';
type KindA = { kind: 'a' };
type KindB = { kind: 'b' };
type KindC = { kind: 'c' };
type AllKinds = KindA | KindB | KindC;
function foldKind<A, B, C>(a: A, b: B, c: C): (kind: Kind) => A | B | C {
return function (kind: Kind): A | B | C {
switch (kind) {
case 'a': return a;
case 'b': return b;
case 'c': return c;
}
}
}
const create: (kind: Kind) => AllKinds = foldKind<KindA, KindB, KindC>(
{ kind: 'a' },
{ kind: 'b' },
{ kind: 'c' }
);
Now, you can only provide a value of KindA
for 'a'
, a value of KindB
for 'b'
, and a value of KindC
for 'c'
.现在,你只能提供价值
KindA
为'a'
,值KindB
为'b'
,和值KindC
为'c'
。 See the demo for yourself.自己看演示。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.