嵌套泛型类型的Typescript推断不能正确解析



我有一些Typescript没有被推断出我期望的方式。我在这里模拟了一些sudo代码,重复了这个问题:

//////////
// Types
/////////
type RegExpForString<T> = T extends string ? RegExp | T : T;
// Allow all values of type string in T to also allow regex 
type Schema<T> = { 
[P in keyof T]?: RegExpForString<T[P]>;
}
/////////
// Usage
/////////
// Accept only Types that have a property called "prop" that has a value type of string
function test<T extends { prop: string }>() {
const x: T["prop"] = "string"; // No Error (working as Expected) 

const y: T["prop"] = 0; // Error (working as Expected)
// Type 'number' is not assignable to type 'T["prop"]'.
// Type 'number' is not assignable to type 'string'.

let mow: Schema<T> = {};
mow.prop = 'test'; // Error (This one is the unexpected one I think should be working)
// Type 'string' is not assignable to type 'RegExpForString<T["prop"]>'.
}

所以上面的代码是简化的(实际代码有这些类型更多的嵌套和额外的联合),但代表了我得到的错误。

所以我对以上的理解是:

  • 在函数T中保证有一个带有字符串值的键prop(因为extend)
  • T['prop']的类型此时可以推断为与string相同(我们在constxy函数上看到这一点)
  • 当我定义mow时,允许有一个值类型为RegExpForString<T['prop']>的键prop(也可以是undefined)(因为我们知道T有一个键prop)
  • RegExpForString<T['prop']><==>RegExpForString<string>& lt; = =比;RegExp | string因此mow.prop应该能够被设置为字符串或正则表达式。

我认为我正确地理解了这一点,我认为也许typescript推断嵌套类型的能力有限制(我试着查找这个,但找不到任何东西)。也有可能我误读/误解了一些东西(仍在学习Typescript),如果有人能纠正我,那就太好了。

打印稿操场

让我们澄清第一个误解。

T['prop']的类型此时可以推断为与string相同(我们在const x和y函数上看到了这一点)

你忘记了文字类型

function test<T extends { prop: string }>() {
const x: T["prop"] = "string";
}
test<{ prop: "a" | "b" }>()

prop只限制extendstring。但调用方也可以指定文字类型或此类的联合。如果prop的类型为"a" | "b",则将"string"赋值给T["prop"]类型的变量可能是不合理的。

那么为什么这个作业可以工作呢?TypeScript团队已经决定急切地在访问属性类型时,根据它们的约束来分析泛型类型。T["prop"]的类型是扩展为string类型,这使得使用它就好像它是string一样可能。

另一种选择是将类型保留为T["prop"]。但是没有可以赋值给这个类型,除了T["prop"],这使得它在大多数情况下都没有用。

所以你看到的情况是编译器比它应该的更宽松导致可能的不健全的情况。


"test"可分配给mow.prop的问题有不同的原因。

function test<T extends { prop: string }>() {
let mow: Schema<T> = {};
mow.prop = "test"; 
}

Schema<T>包含映射类型,其中使用条件计算属性类型。但是完全计算Schema<T>的结果是不可能的,因为T是由函数调用者指定的泛型类型。我们可以看到T约束被用来近似映射类型的结果。但是编译器不会改变条件语句。Schema<T>的属性类型基本上是不透明的,因为编译器延迟了条件的求值。

对于所有string类型的属性,条件将解析为RegExp | T似乎对人类逻辑来说是合理的。但是,只要条件类型依赖于泛型类型,编译器就不会尝试对此进行推理。您可以在#28884或#43846中找到类似的条件句问题。

最新更新