为什么这个通用 TypeScript 函数不起作用,而等效的非通用版本工作正常?



我有两个函数,foo1foo2,它们几乎相同,只是后者是泛型的。

type MyType = { a: number }
type PartialWithBar<T> = Partial<T> & { bar: boolean }
export const foo1 = (obj: MyType) => {
const result: PartialWithBar<MyType> = { bar: false }
Object.assign(result, obj)
return result
}
export const foo2 = <T extends { [key: string]: unknown }>(obj: T) => {
const result: PartialWithBar<T> = { bar: false }
Object.assign(result, obj)
return result
}

foo1没有给出任何错误,但在foo2中我得到以下错误:

Type '{ bar: false; }' is not assignable to type 'PartialWithBar<T>'.
Type '{ bar: false; }' is not assignable to type 'Partial<T>'.ts(2322)

为什么我会收到此错误?我希望result具有属性bar它应该是布尔值,并且我希望它可以选择具有T的属性,但我似乎无法让它工作。

我知道我可以只使用类型断言(const result = { bar: false } as PartialWithBar<T>),但如果可能的话,我不想这样做。

为了使一个值可分配给交集类型Partial<T> & {bar: boolean},它必须可分配给两种类型,因为交集对应于逻辑"and"。{bar: false}的值可分配给{bar: boolean},但不一定可分配给Partial<T>

后者取决于T是什么。特别是,当T = {bar: number}时,类型PartialWithBar<T>实际上是never

type Example = PartialWithBar<{bar: number}>
// type Example = never

所以 Typescript 是正确的:你的结果不一定可以分配给这个类型,因为T可能是使它不可分配的东西。

幸运的是,这里有一个很好的解决方案可以避免类型断言:Object.assign本身返回一个交集类型,并且 Typescript 能够知道T可分配给Partial<T>

export const foo2 = <T extends object>(obj: T): PartialWithBar<T> => {
return Object.assign({bar: false}, obj);
}

但是,请注意,虽然Object.assign的返回类型很方便,但出于与上面讨论的原因相同的原因,它通常不合理。为了提高安全性,您还可以从类型T中删除属性bar,以防存在。请注意,上面的代码与您的原始代码一样,如果bar属性有属性,则会阻止它obj,因此正确的类型如下所示:

type PartialWithBar<T> = Partial<T> & Omit<{bar: boolean}, keyof T>

游乐场链接

相反,如果您希望在bar属性存在时覆盖它,则应改为编写以下内容:

type PartialWithBar<T> = Partial<Omit<T, 'bar'>> & { bar: boolean }
export const foo2 = <T extends object>(obj: T): PartialWithBar<T> => {
return Object.assign({}, obj, {bar: false})
}

游乐场链接

为我的问题添加另一个答案:

export const foo2 = <T extends { [key: string]: unknown }>(obj: T) => {
const part: Partial<T> = {};
const result: PartialWithBar<T> = { ...part, bar: false };
// Optionally do stuff e.g. Object.assign(result, obj)
return result;
};

鉴于这个答案也有效,我仍然不相信 TypeScript 中没有导致我最初发布的错误的错误。

相关内容

  • 没有找到相关文章

最新更新