[译]<<Effective TypeScript>> 高效TypeScript62个技巧 技巧23

132 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第15天,点击查看活动详情

本文的翻译于<<Effective TypeScript>>, 特别感谢!! ps: 本文会用简洁, 易懂的语言描述原书的所有要点. 如果能看懂这文章,将节省许多阅读时间. 如果看不懂,务必给我留言, 我回去修改.

技巧23: 一次性新建object

在ts中, 一个变量的值可能会变, 但是类型一般不会变. 所以你应该一次性新建object, 而不是一步一步的建立.

这是js新建一个object的方法:

const pt = {};
pt.x = 3;
pt.y = 4;

在ts中, 会报错:

const pt = {};
pt.x = 3;
// ~ Property 'x' does not exist on type '{}'
pt.y = 4;
// ~ Property 'y' does not exist on type '{}'

这是因为, 在第一行, pt的类型被推断为 {}. 只有已知的属性值能够修改.

如果进行显示类型标注, 会得到相反的报错:

interface Point { x: number; y: number; }
const pt: Point = {};
   // ~~ Type '{}' is missing the following properties from type 'Point': x, y
pt.x = 3;
pt.y = 4;

解决办法就是, 一次性定义好object:

const pt = {
  x: 3,
  y: 4,
};  // OK

如果想一步一步来创建object, 可以断言来解决:

const pt = {} as Point;
pt.x = 3;
pt.y = 4;  // OK

更好的方法就是: 显示类型注释同时一次性新建object:

const pt: Point = {
  x: 3,
  y: 4,
};

如果想用几个小object新建一个大object, 也应该尽量一步建立好, 避免多步建立:

const pt = {x: 3, y: 4};
const id = {name: 'Pythagoras'};
const namedPoint = {};
Object.assign(namedPoint, pt, id);
namedPoint.name;
        // ~~~~ Property 'name' does not exist on type '{}'

可以通过object扩展操作符,'...':

const namedPoint = {...pt, ...id};
namedPoint.name;  // OK, type is string

也可以使用...一步一步创建object, 关键是在每一步升级的时候, 用新的变量得到新的type:

const pt0 = {};
const pt1 = {...pt0, x: 3};
const pt: Point = {...pt1, y: 4};  // OK

这么迂回的方法新建一个简单的object,显得没有必要. 但是当你需要给object添加一个属性,这又是一个非常有用的技术.

如果想要有条件的添加属性, 可以这样:

declare let hasMiddle: boolean;
const firstLast = {first: 'Harry', last: 'Truman'};
const president = {...firstLast, ...(hasMiddle ? {middle: 'S'} : {})};

将鼠标悬浮到 president 上, 你会看到其类型:

const president: {
    middle: string;
    first: string;
    last: string;
} | {
    first: string;
    last: string;
}

这里的 middle 不是可选字段:

resident.middle
       // ~~~~~~ Property 'middle' does not exist on type
       //        '{ first: string; last: string; }'

如果想把option变成可选字段:

function addOptional<T extends object, U extends object>(
  a: T, b: U | null
): T & Partial<U> {
  return {...a, ...b};
}
const president = addOptional(firstLast, hasMiddle ? {middle: 
'S'} : null);
president.middle  // OK, type is string | undefined