[TS手册学习] 04_对象类型

  • [TS手册学习] 04_对象类型已关闭评论
  • 78 次浏览
  • A+
所属分类:Web前端
摘要

TS官方手册:TypeScript: Handbook – The TypeScript Handbook (typescriptlang.org)对象类型的声明可以是匿名的,也可以使用interface或type进行具名声明。


TS官方手册:TypeScript: Handbook - The TypeScript Handbook (typescriptlang.org)

匿名与具名

对象类型的声明可以是匿名的,也可以使用interfacetype进行具名声明。

function greet(person: { name: string; age: number }) {   return "Hello " + person.name; } 
interface Person {   name: string;   age: number; }   function greet(person: Person) {   return "Hello " + person.name; } 
type Person = {   name: string;   age: number; };   function greet(person: Person) {   return "Hello " + person.name; } 

可选属性 optional

使用?标记:

interface PaintOptions {   shape: Shape;   xPos?: number;   yPos?: number; } 

注:使用PaintOptions声明的对象,它的xPos属性会被初步推断为number | undefined类型。

可以使用条件语句或者解构+默认值的方式收束类型,排除undefined的情况:

function paintShape(opts: PaintOptions) {     let xPos = opts.xPos === undefined ? 0 : opts.xPos;     // xPos 的类型为number } 
function paintShape({ shape, xPos = 0, yPos = 0 }: PaintOptions) { 	// xPos 的类型为number,因为undefined会被默认值0取代     console.log("x coordinate at", xPos); } 

只读属性 readonly

interface SomeType {     readonly prop: string; } 

注:如果有一个属性的值是引用值,例如一个对象或数组,且这个属性被标记为只读(readonly),我们不能修改它的引用值,但是可以修改它的内部属性。

interface Home {   readonly resident: { name: string; age: number }; }  function visitForBirthday(home: Home) {   // 我们可以读取并且更新'home.resident'里的属性   console.log(`Happy birthday ${home.resident.name}!`);   home.resident.age++; }   function evict(home: Home) {   // 但我们不能更新'home.resident'本身   home.resident = { Cannot assign to 'resident' because it is a read-only property.     name: "Victor the Evictor",     age: 42,   }; } 

索引签名 index signatures

interface StringArray {   [index: number]: string; }   const myArray: StringArray = getStringArray(); const secondItem = myArray[1]; 

索引的类型只能是:stringnumbersymbol,以及与这些类型相关的联合类型。

通常只会考虑stringnumber类型的索引。

:可以同时支持stringnumber两种类型的索引器,但数字索引器返回的类型必须是字符串索引器返回类型的子类型。这是因为当使用数字进行索引时,JS 实际上会在索引到对象之前将其转换为字符串。于是使用100"100"进行索引的结果是一样的。

当指定string类型索引的类型为S后,所有属性的类型都需要是S的子集。

interface NumberDictionary {     [index: string]: number;      length: number; // ok     name: string; // NOT ok } 

这是因为当我们访问obj.a的时候,其实也是在访问obj["a"]

在上面这个例子中,string类型索引被声明为number类型,这意味着这个对象的所有属性都是number类型,而下方name却被声明为string类型,矛盾了。

可以使用联合类型解决这个问题:

interface NumberDictionary {     [index: string]: number | string;      length: number; // ok     name: string; // ok } 

同时,索引签名也可以设置为只读:

interface ReadonlyStringArray {   readonly [index: number]: string; } 

类型继承

使用extends,支持多继承:

interface Colorful {     color: string; }   interface Circle {     radius: number; }   interface ColorfulCircle extends Colorful, Circle {}   const cc: ColorfulCircle = {     color: "red",     radius: 42, }; 

交集类型 intersection types

使用&运算符获取已知的两个类型的交集。

interface Colorful {   color: string; } interface Circle {   radius: number; }   type ColorfulCircle = Colorful & Circle; 

将两个基本数据类型取交集会得到never

类型继承 VS 交集类型

二者都可以产生更复杂的类型。

前者是在原有的类型的基础上添加可能未有的属性形成新属性,而后者将已有的类型进行组合。

泛型对象类型 Generic Object Types

interface Box<Type> {   contents: Type; }  let box: Box<string>; 

也可以与泛型函数结合使用:

function setContents<Type>(box: Box<Type>, newContents: Type) {   box.contents = newContents; } 

除了使用interface,也可以使用type别名定义泛型类型。interface一般用来声明对象类型,而type更灵活,可以组合多种类型:

type OrNull<Type> = Type | null; type OneOrMany<Type> = Type | Type[];  // type OneOrManyOrNull<Type> = OneOrMany<Type> | null type OneOrManyOrNull<Type> = OrNull<OneOrMany<Type>>;  // type OneOrManyOrNullStrings = OneOrMany<string> | null type OneOrManyOrNullStrings = OneOrManyOrNull<string>;