带有扩展参数和默认参数的 TypeScript 泛型约束

TypeScript generic contraints with extending and default parameters

所以我正在尝试编写一个具有泛型的函数,它扩展了某个对象从而限制了它。接下来我想将这个泛型与参数的定义一起使用来生成一个新的 "enhanced" 参数。 这一切都很好,但是一旦我想为参数引入默认值,TypeScript 就会抱怨一条消息如下(playground 中的一些不同变体):

函数:

const test1 = <T extends { foo?: string }>(options: T & { bar?: boolean } = {foo: 
''}) => {
    console.log(options);
}

错误:

Type '{ foo: string; }' is not assignable to type 'T & { bar?: boolean; }'. Object literal may only specify known properties, but 'foo' does not exist in type 'T & { bar?: boolean; }'. Did you mean to write 'foo'?

编译器警告我可能想使用 foo,而我确实这么做了。是否根本不可能以这种方式使用泛型,或者这是 TypeScript 中的错误?

none 的初始化起作用的原因是您无法初始化您不知道的东西。考虑以下调用:

test1<{ foo: string, goo: boolean}>();

通用参数对函数有效,但它有一个额外的属性、goo,这是强制性的,但在选项的默认值中未指定。这就是编译器抱怨赋值的原因,你不知道 T 的最终形状,你只知道它的最低要求,所以你不能构建一个与 [=13= 兼容的对象=]

如果您同意 options 没有在泛型类型参数上指定所有强制属性,您可以使用类型断言

const test1 = <T extends { foo?: string }>(options: T & { bar?: boolean } = <any>{foo: ''}) => {
    console.log(options);
}

Type T 在定义时是未知的,所以编译器抛出这个错误,你无法初始化你不知道的东西。我可以想到一些解决方法,但不确定它们对您的情况有多大用处。

您可以保留类型 T 不变,并为 options 参数使用联合类型,如下所示:

const test1 = <T> (options: T | { bar?: boolean } | { foo?: string } = { foo: '' }) => {
  console.log(options);
};

另一种解决方法是使用类型断言并手动告诉编译器初始化程序是它需要的类型:

const test2 = <T extends { foo?: string }>(options: T & { bar?: boolean } = { foo: '' } as T & { bar?: boolean }) => {
  console.log(options);
};

但请记住,这些只是变通办法,无论何时您必须使用变通办法,都意味着设计存在缺陷。也许您可以重新审视您的逻辑并对其进行改进,以消除对这些解决方法的需求。或者,您可以跳过参数初始化并添加 options.foo = options.foo || ''; 作为函数中代码的第一行。只是一些想法。