第三节:TypeScript对象类型

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了第三节:TypeScript对象类型相关的知识,希望对你有一定的参考价值。

参考技术A

javascript 中,我们分组和传递数据的基本方式是通过对象。在 TypeScript 中,我们通过 对象类型 来表示它们。

而TypeScript的核心原则之一是对值所具有的的结构进行类型检查,


之前的数据类型中已经了解,如何限定变量的类型

例如:


如果变量的值不是基本数据类型的值,而是一个对象,可以使用 object 类型



这种写法只能限定变量是object 类型, 但是没法明确表明或限定对象内部属性以及值类型,

也就是说示例中name可是是任意数据类型;属性也不限定于 name 和 age

如果要限定对象属性值的类型,就需要使用字面量的方式进行类型注释

这样的写法虽然达到了限定对象内部结构, 但同时也带来了另外的问题,如属性过多,或多次复用相同类型注释.

因此可以定义接口或 类型别名来定义统一的对象属性限制


通过接口命名对象类型


或者使用类型别名来命名对象类型

但是也需要注意到,在给使用了接口和类型别名时, 变量值接受对象中的属性必须和接口或类型别名中定义的属性一致.,多了少了编译时都会报错


为了让对象类型更具灵活性, 对象类型中每个属性都可以指定几件事:


很多时候,在处理对象类型的时候, 某些属性可能并不一定存在. 这个时候就需要用到可选属性.

可选属性通过在属性名称末尾添加 ? 来将这些属性标记为可选

例如:

可选属性在进行检测时,可选属性在实现上可有可无,这样就提升了对象类型使用的灵活性.

例如:


在TypeScript中对象属性也可以标记为 readonly 只读属性

只读属性虽然不会在运行时更改任何行为.但在类型检查期间无法写入只读标记的属性

通过在属性名前添加 readonly 来标记只读属性

例如:

此时 name 属性标记为只读属性, 当你尝试修改只读属性值时, TypeScript 报错,提示你不可修改

使用 readonly 修饰符并不一定意味着一个值完全不可修改, 也就是说属性属性值是一个引用类型的值,比如对象

readonly 只是表示当前属性本身不能被重写, 但属性值是引用类型, 引用类型内部的值是完全可变的

例如:

示例中只读属性 friend 内部的属性 name 被修改了, 没有任何报错.

因为TypeScript在检查这些类型是否兼容时不会考虑两种类型内部的属性是否有 readonly 标记存在, 所以 readonly 属性也可以通过别名来更改, 也就是说将有只读属性的类型重新分配给没有只读属性的类型


有的时候你并不提前知道类型属性的所有属性名, 但你确实知道属性值的类型

在这样的情况下, 你可以使用索引签名来描述可能值的类型,

例如:

示例中所以签名的意思,表示使用 number 类型的索引获取值时,返回 string 类型

TypeScript索引签名可以同时支持两种类型的索引器.

虽然字符串索引签名是描述\'字典\'模式的强大方式, 但TypeScript 还是强制所有的属性与其返回的类型匹配

例如:

示例中, age 属性的类型是 number 类型, 将会出现错误,因为与索引签名冲突,

其实也很好理解, 因为在使用 age 属性时, 无论通过, obj.age , 还是 obj["age"] , 其都符合索引签名的模式, 返回的值类型应该是 string 类型, 可是你有明确的声明了 age 属性的返回值是明确的 number 类型

此时TypeScript不知道使用索引签名的规则来检查值类型,还是具体罗列 age 属性的类型来检查值类型


这种问题,可以通过给索引签名使用联合类型解决


最后你 还可以在索引签名上使用只读属性 readonly , 表示不可以给索引分配值

例如:只读索引签名


在实际使用时,一个类型有可能是其他类型的具体版本的类型很常见,

简单说就是, 一个类型只有另外一个类型中的部分信息

例如:

有一下两个类型:

人员基本信息,包含姓名,年纪信息

具体学生信息: 包含除了姓名,年纪外还有学号,班级等信息

示例中, StudentPerson 包含 Person 所有的属性信息, 也可以说是 Person 类型更详细的类型,

试想一下,如果每个定义包含 name , age 属性以及其他不同属性类型时,我们都像示例中把 name , age 属性重新定义一遍.

这样的使用方式会导致 name , age 属性大量重复


解决这样重复声明一个类型中所有的属性,我们就可以使用 extends 关键字扩展原有类型, 并添加新的属性

关键字 extends 允许我们有效的从其他命名类型复制成员, 并添加我们想要的新成员


同样 interface 接口也允许从多个接口中扩展新的类型

这样我们就可以使用 WorkPerson 接口来注释一个具有姓名,年纪, 工作信息,工号属性对象的类型


interface 允许我们通过 extends 扩展其他类型来构建新的类型

TypeScript中还为类型别名提供了另外一种称之为交叉类型的类型扩展方式. 主要用于组合现有类型

使用 & 运算符定义交叉类型

示例中,通过交叉类型组合 Colorful 并 Circle 生成一个新的类型别名,类型别名同时具有前两个类型的所有属性


也可以在类型注释的时候使用交叉类型


理解两者的主要区别,方便我们在使用时做出取舍


通用对象类型:就是需要定义一个可以通用类型,

例如:定义一个Box类型,具有 contents 属性, 但是属性的值可能是 string , number , 等各种类型

首先会想到的是属性值类型使用联合类型

但联合类型也仅仅是罗列我们已知的类型, 在使用场景下可能并不通用, 例如值也有可能是其他对象类型呢


此时也许会考虑 any 任何类型

any类型可以工作,但是可能会导致意外事故发生


也可以尝试定义 unknown 类型

使用unknown类型就意味着我们需要进行类型检查,或者使用类型断言


不过 unknow 也不是特别安全, 比较安全的做法是为每一种类型添加一个接口

但这意味着如果是给函数参数使用, 我们需要创建不同函数或函数重载, 才能对这些进行操作

这样的处理方式不仅繁琐, 而且之后需要需要新的类型还需要引入新的类型和重载, 因为我们contents类型和重载实际上都是相同的,


最好的处理方式,就是我们创建一个声明类型参数的泛型,

其实就是将类型定义为像函数参数或变量一样, 类型参数就可以在多个地方使用, 通过传递具体类型,让使用类型参数的地方全部指代当前具体类型

此时当我们在使用 Box 类型注释时,必须给出一个类型参数来代替 Type

此时会将Box视为类型模板,其中 Type 为占位符将被其他类型替换,


Box 类型可以重复使用, Type 可以用任何类型代替, 这意味着当我们需要一个新类型 Box 是, 我们根本不要在 声明一个新的 Box 类型, 我们只需要传递不同的类型替换 Type 即可


这也意味着,如果将类型用在函数参数上,我们可以通过使用泛型函数来避免重载

示例中, 我们并没有限定obj的类型, 但是在传递参数后, TypeScript根据入参推断出 Type 是一个string 类型, 因此函数的第二个参数也必须是一个字符串类,否则TypeScript将发出错误警告

例如:如下调用函数


类型的别名也是可以通用的

例如

Box 接口也可以使用类型别名来替换


由于类型别名与接口不同,类型别名不仅仅可以描述对象,还可以使用类型别名来编写其他类型的通用帮助类型

软件构造 第三章第三节 抽象数据型(ADT)

软件构造 第三章第三节 抽象数据型(ADT)

Creators(构造器):

创建某个类型的新对象,?个创建者可能会接受?个对象作为参数,但是这个对象的类型不能是它创建对象对应的类型。可能实现为构造函数或静态函数。(通常称为工厂方法)

t* ->  T

例子:Integer.valueOf( )

 

Producers(生产器):

通过接受同类型的对象创建新的对象。

T+ , t* -> T

例子:String.concat( )

 

Observers(观察器):

获取抽象类型的对象然后返回一个不同类型的对象/值。

T+ , t* -> t

例子:List.size( ) ;

 

Mutators(变值器):

改变对象属性的方法 ,

变值器通常返回void,若为void,则必然意味着它改变了对象的某些内部状态;当然,也可能返回非空类型 

T+ , t* -> t || T || void

例子:List.add( )

 

Representation Independence

表示独立性:client使用ADT时无需考虑其内部如何实现,ADT内部表示的变化不应影响外部spec和客户端。

除非ADT的操作指明了具体的前置条件/后置条件,否则不能改变ADT的内部表示——spec规定了 client和implementer之间的契约。

 

 

Invariance和表示泄露(Rep exposure):

-一个好的抽象数据类型的最重要的属性是它保持不变量。一旦一个不变类型的对象被创建,它总是代表一个不变的值。当一个ADT能够确保它内部的不变量恒定不变(不受使用者/外部影响),我们就说这个ADT保护/保留自己的不变量。

-Defensive Copy

 

 

 

 

抽象函数AF与表示不变量RI

  • 对于RI(表示不变量),仅仅宽泛的说什么区域是合法的并不够,你还应该说明是什么使得它合法/不合法。
  • 对于AF(抽象函数)来说,仅仅宽泛的说抽象域表示了什么并不够。抽象函数的作用是规定合法的表示值会如何被解释到抽象域。作为一个函数,我们应该清晰的知道从一个输入到一个输入是怎么对应的。

 

做出具体的解释:每个rep value如何映射到abstract value

技术分享图片

Safety from Rep Exposure

技术分享图片

以上是关于第三节:TypeScript对象类型的主要内容,如果未能解决你的问题,请参考以下文章

第三节课: Python 基本数据类型讲解(1/3)

必备前端基础知识-第三节2:JavaScript数组函数和对象

必备前端基础知识-第三节2:JavaScript数组函数和对象

javascript第三节课

第一百零三节,JavaScript对象和数组

TypeScript——接口