想象以下代码(playground):type AvailableTypes = { 'array': Array<any>; 'string': string; 'object': object;}class Wrapper<T extends keyof AvailableTypes> { // Is either array, string or object private readonly type: T; // ERROR: Property 'value' has no initializer and is not definitely assigned in the constructor. private readonly value: AvailableTypes[T]; constructor(type: T) { this.type = type; /** * ERROR: * TS2322: Type 'never[]' is not assignable to type 'AvailableTypes[T]'. * Type 'never[]' is not assignable to type 'never'. */ switch (type) { case 'array': this.value = []; break; case 'string': this.value = ''; break; case 'object': this.value = {}; break; } }}有两个主要错误: TS2322:类型'never []'无法分配给类型'AvailableTypes [T]'。 类型“ never []”不可分配给类型“ never”即使AvailableTypes[T]始终解析为声明的一种类型,而AvailableTypes是它的键。...和 属性“值”没有初始化程序,并且在构造函数中未明确分配。尽管T是强制性的,并且必须为type,string或array我在这里想念什么?可能相关的SO线程:Typescript Generic UnionCreate union out of interface using tag genericTypeScript: use of generic and union types更新资料(更新为@jcalz answer)应该可以根据object.属性对value进行类型检查:// In the Wrapper class, should work since value can only be an array if type is 'array':public pushValue(val: unknown) { if (this.type === 'array') { this.value.push(val); }}Playground 最佳答案 潜在的问题是不能通过控制流分析来缩小泛型类型参数,这是TypeScript中一个长期存在的开放性问题,有关更多信息,请参见microsoft/TypeScript#13995。在type / switch语句中检查case时,编译器可以将type变量的类型缩小为文字类型"array",但不会将类型参数T的范围缩小为。因此,它无法验证为类型"array"分配any[]值是否安全。编译器必须执行一些当前不执行的分析,例如“好吧,如果AvailableTypes[T],并且我们从type === "array"的类型推断出T,那么在此type块内,我们可以缩小到case,因此T的类型是"array",又名this.value,因此可以安全地为其分配AvailableTypes["array"]。”但这不会发生。相同的问题导致“没有明确分配any[]”错误。编译器没有足够的资金来查看[] / value耗尽了switch的所有可能性,因为它在这里没有进行控制流分析。这里最简单的解决方法是使用type assertions告诉编译器您知道自己在做什么,因为它无法验证。要处理穷举性问题,您可以抛出一个case案例,如下所示:class Wrapper<T extends keyof AvailableTypes> { private readonly type: T; private readonly value: AvailableTypes[T]; constructor(type: T) { this.type = type; switch (type) { case 'array': this.value = [] as AvailableTypes[T]; // assert break; case 'string': this.value = '' as AvailableTypes[T]; // assert break; case 'object': this.value = {} as AvailableTypes[T]; // assert break; default: throw new Error("HOW DID THIS HAPPEN"); // exhaustive } }}或者可以将T从default扩展到type,这将使编译器执行必要的控制流分析,以使其了解情况是详尽的:class Wrapper<T extends keyof AvailableTypes> { private readonly type: T; private readonly value: AvailableTypes[T]; constructor(type: T) { this.type = type; const _type: keyof AvailableTypes = type; // widen to concrete type switch (_type) { case 'array': this.value = [] as AvailableTypes[T]; // assert break; case 'string': this.value = '' as AvailableTypes[T]; // assert break; case 'object': this.value = {} as AvailableTypes[T]; // assert break; } }}另一个解决方法(mentioned in a comment by person who implemented the soundness change)利用以下事实:如果您具有类型T的值keyof AvailableTypes和类型t的键T,则k的类型为被编译器视为K extends keyof T。因此,如果我们可以创建有效的t[k],则可以使用T[K]对其进行索引。可能是这样的:class Wrapper<T extends keyof AvailableTypes> { private readonly type: T private readonly value: AvailableTypes[T]; constructor(type: T) { this.type = type; const initValues: AvailableTypes = { array: [], string: "", object: {} }; this.value = initValues[type]; }}到目前为止,这是比类型断言和AvailableTypes语句更好的方法,并且可以安全启动。因此,除非您的用例禁止使用此解决方案,否则我会选择该解决方案。好的,希望其中一种帮助。祝好运!Playground link to code更新:discriminated union instead of generic classes关于typescript - 基于泛型的类字段类型,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/59910798/ 10-12 04:43