在 TypeScript 中检查文字对象类型的更简单方法

Simpler way to check literal object type in TypeScript

我想 Object.assign 到一个已知类型的对象,即来自对象文字的一组属性,这些属性应该属于同一类型。在 TypeScript 中是否有更简洁的方法来执行此操作,而无需实际调用身份函数或创建单独的变量,如 related question?

中所建议
type Person = {
  first?: string;
  last?: string;
}

// Overkill, actually generates code to call a function
function check<T>(value: T): T { return value; }

const dude: Person = {
  first: 'Mike',
};

Object.assign(dude, check<Person>({  // <- trying not to call a function
  last: 'Johnson',
  age: 27,  // <-- should be flagged as unknown property
}));

TL;DR - 寻找一种直接对对象文字进行类型检查的方法。

如果你用 Object.assign 绕过它,为什么老兄是一个常数?

通常,如果您尝试将 'Person' 以外的任何内容分配给 dude,您会收到错误消息,例如

type Person = {
  first?: string;
  last?: string;
}

let dude: Person = {
  first: 'Mike',
};

dude = {  
   last: 'Johnson',
   age: 27
} // Error: Not assignable to type Person

这将对分配进行类型检查,如果您想分配给常量,也会显示错误。

我想不出在所有情况下都适用的 "perfect" 解决方案。大多数说服编译器执行您想要的类型检查的机制还伴随着一些您说过您不想要的添加的运行时代码(例如新的中间变量赋值,或调用标识函数)。


一个问题是 TypeScript 没有内联类型注释。 (参见 {...} as Person 形式的 microsoft/TypeScript#7481 and microsoft/TypeScript#13208 for discussion.) You'd like to be able to ask the compiler to verify that an expression {...} is of type Person, and have the compiler complain if it cannot be verified. The closest operator we have in TypeScript is a type assertion。但是这 告诉编译器 表达式 {...} 属于 Person 类型;您想


即使我们有一个内联注释运算符,还有另一个问题:TypeScript 中的对象类型并不准确。 (有关讨论,请参阅 microsoft/TypeScript#12936。)TypeScript 中的对象类型是 open,因为您可以在不破坏兼容性的情况下向它们添加更多属性。如果您有一个 Person 类型的对象,您对它的 firstlast 属性有所了解,但您对任何其他属性一无所知。仅仅因为 Person 的定义没有提到 age 属性,它 并不意味着 Person 类型的对象不能有 age 属性。很可能会有这样的界面:

interface AgedPerson extends Person {
  age: number;
}

TypeScript 类型系统的 structural 性质意味着 {last: "Johnson", age: 27} 是一个有效的 AgedPerson 即使你没有这样声明它(即使 AgedPerson 没有定义)。由于 AgedPersonPerson 的有效子类型,因此 {last: "Johnson", age: 27} 也是有效的 Person

现在,当人们使用像 {last: "Johnson", age: 27} 这样的对象文字时,他们通常不打算添加这些额外的属性,因此 TypeScript 有一个名为 excess property checking 的功能,它将对象文字视为如果您添加未知属性,则会出现确切的类型和抱怨。此功能很有用,但很容易绕过。因此,重要的是要提到,如果您完全重构代码,关于 age 超出 属性 的警告可能会消失:

const ageDude = { last: 'Johnson', age: 27 };
const personDude: Person = ageDude; // no error

也就是说,对于您给出的特定示例,我推荐的解决方案是:

Object.assign<Person, Person>(dude, {
  last: 'Johnson',
  age: 27, // error
});

此处您在调用 Object.assign 时手动指定泛型类型参数,其中第一个类型参数对应于第一个函数参数的类型,第二个类型参数对应于第二个函数参数。您希望编译器将它们都视为 Person,因此您应该编写 Object.assign<Person, Person>(...)。并出现预期的超额 属性 错误。


好的,希望对您有所帮助;祝你好运!

Playground link to code