Freshness

为了能让检查对象字面量类型更容易,TypeScript 提供 「Freshness」 的概念(它也被称为更严格的对象字面量检查)用来确保对象字面量在结构上类型兼容。

结构类型非常方便。考虑如下例子代码,它可以让你非常便利的从 JavaScript 迁移至 TypeScript,并且会提供类型安全:

  1. function logName(something: { name: string }) {
  2. console.log(something.name);
  3. }
  4. const person = { name: 'matt', job: 'being awesome' };
  5. const animal = { name: 'cow', diet: 'vegan, but has milk of own specie' };
  6. const randow = { note: `I don't have a name property` };
  7. logName(person); // ok
  8. logName(animal); // ok
  9. logName(randow); // Error: 没有 `name` 属性

但是,结构类型有一个缺点,它能误导你认为某些东西接收的数据比它实际的多。如下例,TypeScript 发出错误警告:

  1. function logName(something: { name: string }) {
  2. console.log(something.name);
  3. }
  4. logName({ name: 'matt' }); // ok
  5. logName({ name: 'matt', job: 'being awesome' }); // Error: 对象字面量只能指定已知属性,`job` 属性在这里并不存在。

WARNING

请注意,这种错误提示,只会发生在对象字面量上。

如果没有这种错误提示,我们可能会去寻找函数的调用 logName({ name: 'matt', job: 'being awesome' }),继而会认为 logName 可能会使用 job 属性做一些事情,然而实际上 logName 并没有使用它。

另外一个使用比较多的场景是与具有可选成员的接口一起使用,如果没有这样的对象字面量检查,当你输入错误单词的时候,并不会发出错误警告:

  1. function logIfHasName(something: { name?: string }) {
  2. if (something.name) {
  3. console.log(something.name);
  4. }
  5. }
  6. const person = { name: 'matt', job: 'being awesome' };
  7. const animal = { name: 'cow', diet: 'vegan, but has milk of own species' };
  8. logIfHasName(person); // okay
  9. logIfHasName(animal); // okay
  10. logIfHasName({ neme: 'I just misspelled name to neme' }); // Error: 对象字面量只能指定已知属性,`neme` 属性不存在。

之所以只对对象字面量进行类型检查,因为在这种情况下,那些实际上并没有被使用到的属性有可能会拼写错误或者会被误用。

允许额外的属性

一个类型能够包含索引签名,以明确表明可以使用额外的属性:

  1. let x: { foo: number, [x: string]: any };
  2. x = { foo: 1, baz: 2 }; // ok, 'baz' 属性匹配于索引签名

用例:React State

Facebook ReactJS 为对象的 Freshness 提供了一个很好的用例,通常在组件中,你只使用少量属性,而不是传入所有,来调用 setState

  1. // 假设
  2. interface State {
  3. foo: string;
  4. bar: string;
  5. }
  6. // 你可能想做:
  7. this.setState({ foo: 'Hello' }); // Error: 没有属性 'bar'
  8. // 因为 state 包含 'foo' 与 'bar',TypeScript 会强制你这么做:
  9. this.setState({ foo: 'Hello', bar: this.state.bar });

如果你想使用 Freshness,你可能需要将所有成员标记为可选,这仍然会捕捉到拼写错误:

  1. // 假设
  2. interface State {
  3. foo?: string;
  4. bar?: string;
  5. }
  6. // 你可能想做
  7. this.setState({ foo: 'Hello' }); // Yay works fine!
  8. // 由于 Freshness,你也可以防止错别字
  9. this.setState({ foos: 'Hello' }}; // Error: 对象只能指定已知属性
  10. // 仍然会有类型检查
  11. this.setState({ foo: 123 }}; // Error: 无法将 number 类型赋值给 string 类型

原文: https://jkchao.github.io/typescript-book-chinese/typings/freshness.html