目录
一、对象的表达形式
对象作为常用的数据类型,他的表达形式与函数一样有很多种形式
// 匿名对象(没有定义对象直接使用)
function returnMsg(person: {name: string,age: Number}){
console.log(person.name)
}
// 接口interface 定义对象
interface Person {
name: string,
age: Number
}
function returnMsg(person: Person ){
console.log(person.name)
}
// 类型别名type 定义对象
type Person = {
name: string,
age: Number
}
function returnMsg(person: Person ){
console.log(person.name)
}
一、对象的属性修改器
对象类型中的每个属性都可以指定几件事:类型、属性是否是可选的,以及属性是否可以被写入。
1、可选属性
很多时候,我们会发现在处理的对象中可能有一个属性有时候用不到,有时候有需要用到。在这些情况下,我们可以在这些属性的名 字后面加上一个问号(?),把它们标记为可选的。
interface dataType {
name?: String,
age: Number,
sex: String,
happy?: String
}
function consoleData(data: dataType){
// ......
}
consoleData({name: 'hhh',age: 12,sex: 'nv'}); // 正常输出
consoleData({age: 12,sex: 'nv',happy: 'hhhhhhh'}); // 正常输出
consoleData({age: 12,sex: 'nv'}); // 正常输出
consoleData({name: 'hhh',age: 12}); // 语法错误 sex为必填项
consoleData({name: 'hhh',happy: 'hhhhhhh',sex: 'nv'}); // 语法错误 age为必填项
2、只读属性
只读属性见名知意,只能被读取,不能被修改,在定义时在属性前加上 readonly 关键字。
使用 readonly 修饰符并不一定意味着一个值是完全不可改变的。如果你定义一个对象,这个对象被 readonly 修饰之后,这个对象不能添加、删除属性,但是可以改写对象属性的值。
interface ReadData {
readonly props: String
}
function returnReadData(data: ReadData){
console.log(data.props); // 可正常输出
data.props = "hhhhh"; // 语法错误,props为只读属性
}
// 定义对象为只读 就可改只读对象的属性且不会报语法错误
interface ReadData {
readonly data: {
name: string,
sex: string
}
}
function returnReadData(data: ReadData){
console.log(data.data.name)
data.data.name = "hhh" // 可正常通过语法检查
}
3、索引签名
索引签名的属性类型必须是 string 或 number 。
interface StringArray {
[index: number]: string;
}
const myArray: StringArray = ['a', 'b'];
const secondItem = myArray[1];
console.log(secondItem) // 输出 b
// 对象中如果有多种数据类型,则索引需要定义成联合数据类型
interface ReadonlyStringArray {
[index: number]: string; // 会存在语法错误,需要将索引类型改为string | number
name: string,
age: number
}
let myArray: ReadonlyStringArray = getReadOnlyStringArray();
myArray[2] = "Mallory";
// 只读索引
interface ReadonlyStringArray {
readonly [index: number]: string;
}
let myArray: ReadonlyStringArray = getReadOnlyStringArray();
myArray[2] = "Mallory";
二、扩展类型
有时候一种类型可能是其他类型的更具体的版本,这个时候可能需要去重新定义。为了避免代码的冗余,使用类型扩展来达到新的数据类型的定义。(个人觉得这个扩展类型就有点继承那意思,就像是小明继承了他爸的优缺点,然后有根据自己生活的环境等因素,去发展出自己个性化的优缺点)
// 接口定义数据类型
interface Person {
name: String
}
// 接口扩展数据类型
interface man extends Person{
name: String,
sex: String
}
// 接口的另一种扩展方式 => 重复定义(类型别名不可以使用这种方式)
interface man {
name: String
}
interface man {
sex: String
}
// =======================================================
// 类型别名 定义数据类型
type Person = {
name: String
}
// 类型别名 扩展数据类型
interface man = Person & {
name: String,
sex: String
}
三、交叉类型
交叉类型:主要用于组合现有的对象类型。
交叉类型跟扩展类型很像,但是又有点不同:交叉类型侧重点是组合现有的,而扩展类型则是在现有的基础上去扩展新的。
interface Colorful {
color: String;
}
interface Circle {
radius: number;
}
// 使用交叉& 获得新的数据类型
type unionType = Colorful & Circle;
// 定义一个unionType类型的数据
const temp: unionType = {
color: 'red',
radius: 5
}
交叉类型 VS 接口
相同点: 都可以描述对象或函数,都可以实现扩展
不同点:
接口 | 交叉类型 | |
声明范围 | 值是具体构造对象 | 可为任意类型创造类型别名 |
扩展形式 | extends | & |
重复定义表现形式 | 自动合并 | 报错 |
四、泛型对象类型
假如我们需要定义一个可以包含任意数据的盒子,
- 如果使用any,那么可能存在隐患;
- 如果是使用unknown,就需要我们知道内容的类型,需要提前做预防性检查或是类型推断;
- 最安全的一种方式就是像函数重载那样将所有可能的情况都定义一遍,但是这样会造成代码的冗余。
这个时候就可以引入泛型(我们知道在Java中泛型常用在数组等可迭代的数据类型中,对其中的数据做出类型的一个规范,就有点安全机制那感觉<个人理解,有误望指正>)。
// 这时候的Box就有点盲盒那意思,你许什么愿望,就拿到什么东西(传递什么数据类型,那Box里面的contents就是什么类型)
interface Box<Type> {
contents: Type;
}
interface StringBox {
contents: string;
}
let boxA: Box<string> = { contents: "hello" };
let boxB: StringBox = { contents: "world" };
// Box 中传递除了 基元类型以外的其他对象类型
interface Box<Type> {
contents: Type;
}
interface Apple {
// ....
}
// 等价于 '{ contents: Apple }'.
type AppleBox = Box<Apple>;
function setContents<Type>(box: Box<Type>, newContents: Type) {
box.contents = newContents;
}
五、数组类型
数组类型:
每次我们写出 number[] 或 string[] 这样的类型时,实际上只是 Array<Number> 和 Array<String>的缩写。
1、只读数组类型
ReadonlyArray是一个比较特殊的类型,主要是用于定义不被改变的数组类型。
和属性的 readonly 修饰符一样,它主要是一个可以用来了解意图的工具。当看到一个返回 ReadonlyArray 的函数时,就表示根本不打算改变其内容。
与 Array 不同,没有ReadonlyArray 的构造函数,即不能使用new ReadonlyArr(['hhh'])去构造出一个只读数组;但是可以将普通的 Array 分配给 ReadonlyArray 。
与 readonly 属性修改器不同,可分配性在普通 Array 和 ReadonlyArray 之间不是 双向的(普通Array可以将其值分配给ReadonlyArray,但是反过来就不能成立)。
function expleReadArr(value: ReadonlyArray<String>){
// value 是只读数组,可进行读取
let data = value.slice()
// value 不能被修改
value.push('hhh') // 会报语法错误
}
// 会报语法错误,过不了ts的语法检查
new ReadonlyArray(['hhh','xxx','ttt'])
// 只读数组虽然不能使用像Array那样的构造函数去生成,但可以将普通数组赋值给只读数组
const roArray: ReadonlyArray<string> = ["red", "green", "blue"];
六、元组类型
Tuple 类型是另一种 Array 类型,它可以确切地知道包含多少个元素,以及它在特定位置包含元素的哪些类型。
元组可以通过在元素的类型后面写出问号(?)表示 可选的元组,可选元素只能出现在末尾,而且还影响到长度的类型。
// 类型expleTuple 表示数组只有两个值,且第一个是number类型,第二个是string类型
type expleTuple = [number,string]
// 元组的可选类型(在元素类型的后面加上?,就表示该元素为可选元素)
type data= [number, number, number?];
function setCoordinate(coord: data) {
const [x, y, z] = coord;
console.log(`提供的坐标有 ${coord.length} 个维度`);
}
1、只读元组
tuple 类型有只读特性,可以通过在它们前面加上一个 readonly 修饰符来指定该元组是否为只读元组。
元组往往被创建并不被修改,所以在可能的情况下,将类型注释为只读元组是一个很好的默认。这个很重要,因为带有 const 断言的数组字面量将被推断为只读元组类型。
function doSomething(pair: readonly [string, number]) {
// ...
}
// 使用const将元组变成默认的只读元组
let point = [3, 4] as const; // 这个时候point就为只读元组类型
function distanceFromOrigin([x, y]: [number, number]) {
return Math.sqrt(x ** 2 + y ** 2);
}
distanceFromOrigin(point); // 会报错,因为函数不能确认只读元组point是否被修改