英文:
Enum value as function parameter mapped to specific return type
问题
我创建了一个我正在遇到问题的小示例。我希望get
函数接受一个枚举参数,并且该函数的返回值取决于该枚举值。我试图将枚举值映射到它的类型,在映射类型中,但我没有成功。
下面的代码不起作用-它似乎认为映射类型是一个联合类型。
type T1 = { name: string }
type T2 = { age: number }
enum Type { TypeOne, TypeTwo }
const get = <T extends Type>(type: T): {
[Type.TypeOne]: T1
[Type.TypeTwo]: T2
}[T] => type === Type.TypeOne ? { name: 'Brian' } : { age: 42 }
我在最后一行得到错误消息:
Type '{ name: string; } | { age: number; }' is not assignable to type '{ 0: T1; 1: T2; }[T]'.
Type '{ name: string; }' is not assignable to type '{ 0: T1; 1: T2; }[T]'.
Type '{ name: string; }' is not assignable to type 'T1 & T2'.
Property 'age' is missing in type '{ name: string; }' but required in type 'T2'.ts(2322)
有什么建议吗?
英文:
I have created a small example of a problem I'm experiencing. I want the get
function to take a enum parameter, and have the return value of the function be dependant on that enum value. I'm trying to map the enum value to it's type in a mapped type, but I'm not having any luck.
The code below doesn't work - it seems like it believes the mapping type to be a union type.
type T1 = { name: string }
type T2 = { age: number }
enum Type { TypeOne, TypeTwo }
const get = <T extends Type>(type: T): {
[Type.TypeOne]: T1
[Type.TypeTwo]: T2
}[T] => type === Type.TypeOne ? { name: 'Brian' } : { age: 42 }
I get the error message on the last line:
Type '{ name: string; } | { age: number; }' is not assignable to type '{ 0: T1; 1: T2; }[T]'.
Type '{ name: string; }' is not assignable to type '{ 0: T1; 1: T2; }[T]'.
Type '{ name: string; }' is not assignable to type 'T1 & T2'.
Property 'age' is missing in type '{ name: string; }' but required in type 'T2'.ts(2322)
Any suggestions?
答案1
得分: 1
TypeScript目前无法使用控制流分析来影响泛型 类型参数。因此,虽然检查 type === Type.TypeOne
会影响 type
的表现类型,但泛型类型参数 T
将保持不受影响。
检查 type === Type.TypeOne
应该将 T
从 T extends Type
缩小为 T extends Type.TypeOne
,这似乎是"显而易见"的,但总的来说,这是不正确的。毕竟,T
可能 是完整的联合类型 Type
。对于像 <T extends Type>(type1: T, type2: T) => void
这样的调用签名,假设检查 type1 === Type.TypeOne
意味着 type2 === Type.TypeOne
将是危险的。因此,可能需要采用其他方法,可能如此开放的功能请求中所描述。但目前还没有实现类似这样的功能。
在支持此功能之前,我会采用一种方法来实现返回索引访问类型的泛型函数,即实际上使用适当类型的对象来索引适当的索引。对于您的示例,可以看起来像这样:
const get = <T extends Type>(type: T): {
[Type.TypeOne]: T1
[Type.TypeTwo]: T2
}[T] => ({
[Type.TypeOne]: { name: 'Brian' },
[Type.TypeTwo]: { age: 42 }
}[type]);
如果真的需要的话,甚至可以使用getter,使对象仅在访问的成员时才进行惰性计算:
const get = <T extends Type>(type: T): {
[Type.TypeOne]: T1
[Type.TypeTwo]: T2
}[T] => ({
get [Type.TypeOne]() { return { name: 'Brian' } },
get [Type.TypeTwo]() { return { age: 42 } }
}[type]);
对于示例代码,这不会有太大影响,但如果原始函数具有副作用,或者预先计算每个可能的返回值太昂贵,您可能会希望这样做。
英文:
TypeScript currently can't use control flow analysis to affect generic type parameters. So while checking type === Type.TypeOne
will have an effect on the apparent type of type
, the generic type parameter T
will stubbornly remain unaffected.
It might seem "obvious" that checking type === Type.TypeOne
should narrow T
from T extends Type
to T extends Type.TypeOne
, but in general this would be incorrect. After all, T
might be the full union type Type
. For a call signature like <T extends Type>(type1: T, type2: T) => void
, it would be dangerous to assume that checking type1 === Type.TypeOne
would mean that type2 === Type.TypeOne
. So some other approach is necessary, possibly as described in the open feature request at microsoft/TypeScript#33014. But for now, nothing like this is implemented.
Until and unless support for that is added, the way I'd approach implementing a generic function that returns an indexed access type is to actually index into an appropriately-typed object with the appropriate index. For your example that could look like:
const get = <T extends Type>(type: T): {
[Type.TypeOne]: T1
[Type.TypeTwo]: T2
}[T] => ({
[Type.TypeOne]: { name: 'Brian' },
[Type.TypeTwo]: { age: 42 }
}[type]);
If you really need to, you could even write this with getters so that the object lazily computes only the member you are accessing:
const get = <T extends Type>(type: T): {
[Type.TypeOne]: T1
[Type.TypeTwo]: T2
}[T] => ({
get [Type.TypeOne]() { return { name: 'Brian' } },
get [Type.TypeTwo]() { return { age: 42 } }
}[type]);
For the example code that wouldn't matter much, but you might want to do this if the original function has side effects or if pre-computing every possible return value is too expensive.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论