TS系列教程九:对象

147 阅读4分钟

定义

//以逗号结尾
type Person={
    name:string,
    age:number
}
//以分号结尾
type Person={
    name:string;
    age:number
}
const person:Person={
    name:'Tom',
    age:18
}

对象的定义变量之间可以用逗号隔开也可以用分号,个人建议用逗号

type Person={
    name:string,
    age:number
}
const person:Person={
    name:'Tom',
    age:18
}
delete person.name//报错 
person.name='Jack'//正确 定义的属性只能修改不能删除
person.sex//报错 不能访问不存在的属性

可选属性 属性后边加问号

type Person={
    name:string,
    age?:number
}
const person:Person={
    name:'Tom';
}

和其他可选参数一样,上边的age?:number相当于 age:number|undefined,但是不能写成后者的形式

只读属性 属性前边加readonly

type Person={
    name:string,
    readonly age:number
}
const person:Person={
    name:'Tom',
    age:18
}
person.age=19//报错 age是只读属性

只读属性只能在初始化的时候赋值,后边不能修改

type Person={
    name:string,
    readonly hobby:string[]
}
const person:Person={
    name:'Tom',
    hobby:['打游戏']
}
person.hobby.push('看书')

如果只读属性是一个对象,只要不直接赋值改变地址就可以

type A={
    name:string,
    age:number
}
type B={
    readonly name:string,
    readonly age:number,
}
const a:A={
    name:"Alice",
    age:25
}
const b:B=a
a.age++
console.log('b.age:',b.age);

上边是一种特殊情况,把一个可读写类型的对象赋值给只读的对象,修改可读写对象,这个只读对象也会修改。同样的把只读赋值给可读写,依旧可以修改,也算个bug吧。

可以像读取对象属性一样读取类型的属性赋值给别的类型

type A={
    name:string,
    age:number
}
type name=A['name']

属性名的索引类型

type A={
    a:string,
    b:number,
    [property:number]:string,
}
let a:A={
    a:'hello',
    b:123,
    1:'world',
    5:'456'
}

有时后端返回的参数不固定,就可以使用这个索引类型,property可以写成别的值,建议就写成这个把通俗易通。 只能存在一个索引类型

type A={
    [property:number]:number,
}
let a:A=[0,1,2];
console.log(a.length)//报错

虽然可以把数组赋值它,但是尽量不要这样做,编辑器还是认为它是一个对象,不会有数组的方法的

对象的解构

let {a,b}={a:5,b:6};

解构属性起别名

let {a:c,b:d}=obj;
//等同于
let c=obj.a
let d=obj.b

因为:被占用所以,只能用别的办法定义类型,于是有了下边的这种

let {a:c,b:d}:{a:number,b:number}={a:5,b:6};

这样写其实定义的是c和d,就有点别扭了,老铁们记住就行了

结构类型原则

type A={
    x:number
}
type B={
    x:number,
    y:number,
}

B包含A的所有属性,这种情况下B满足A的结构特性,所有的以B类型定义的值都可以赋值给以A定义的值

type A={
    x:number
}
type B={
    x:number,
    y:number,
}
let b:B={
    x:5,
    y:10,
}
let a:A=b;

这种情况是为了迎合js项目而设计的,不管有多少属性,只要满足我提供的属性,就认为是同一类型,某些特殊情况下也会出现一些奇葩问题

type myObj = {
    x: number,
    y: number,
};

function getSum(obj: myObj) {
    let sum = 0;

    for (const n of Object.keys(obj)) {
        const v = obj[n]; // 报错
        sum += Math.abs(v);
    }

    return sum;
}

ts认为你传递的obj还可能包含除x,y的其他属性,换句话说它认为可能存在myObj里没有的属性,实际情况下调用函数时别的属性也传不进来,会报错的。只能额外记录下这种情况,解决下

type myObj = {
    x: number,
    y: number,
};

function getSum(obj: myObj) {
    let sum = 0;

    for (const n of Object.keys(obj) as (keyof myObj)[]) {
        const v = obj[n]; // 报错
        sum += Math.abs(v);
    }

    return sum;
}

用断言直接告诉ts,它就是myObj的属性

严格字面量检测

type myObj = {
    x: number,
    y: number,
};

let obj: myObj = {
    x: 1,
    y:2,
    c:6//报错
};

如果是直接字面量赋值,会启动严格检验模式,

最小检测

如果一个对象的所有属性都是可选属性,当赋值时,ts的检查机制是要么你是空对象,要么你至少包含一种属性

type Options = {
    a?: number;
    b?: number;
    c?: number;
};

const opts = { d: 123 };

const obj: Options = opts; // 报错

你可以把opts赋值为空对象,或者至少包含a,b,c的一种,或者你更改类型

type Options = {
    [property:string]:number;
};

空对象

const obj={};
obj.name='张三'

因为ts的类型推导,obj的类型会被推导为{},当赋值时就会报错了