数组-Array
数组在 TS
里面分成两种类型,分别是数组(Array)和元组(Tuple)。
两种基本写法
TS
中定义数组类型的方式常见的有两种方式。
第一种是以 T[]
形式如下:
js
let arrNum: number[] = [1, 2, 3];
let arrStr: string[] = ['a', 'b', 'c'];
let arrBool: boolean[] = [true, false];
第二种是以"泛型"的形式(泛型后续会讲):
javascript
let arrNum1: Array<number> = [1, 2, 3];
let arrStr1: Array<string> = ['a', 'b', 'c'];
let arrBool1: Array<boolean> = [true, false];
上面可以看到数组中所有成员的类型必须相同
🍊关于如何定义数组中成员类型不相同的情况?
可以使用"联合类型"来定义(联合类型后续会讲)。
javascript
let result1: (number | string | boolean)[] = [1, 'a', true];
let result2: Array<number | string | boolean> = [1, 'a', true];
或者可以定义成 any[]
类型。
多维数组
多维数组的定义方式推荐使用 T[][][]...
形式,泛型的形式有点像套娃,可读性略差。
javascript
let arr1: number[][] = [[1], [2], [3]];
let arr2: number[][][] = [[[1]], [[2]], [[3]]];
let arr11: Array<Array<number>> = [[1], [2], [3]];
let arr22: Array<Array<Array<number>>> = [[[1]], [[2]], [[3]]];
函数-Function
在 JS
中有三种函数,分别是命名函数、匿名函数、箭头函数,分别如下:
javascript
function sum(a, b) { return a + b }
let each = function(...args) {};
let getName = (name = '橙某人') => name;
那么,它们在 TS
中要如何定义呢?
三种基本写法
javascript
function sum(a: number, b: number): number { return a + b }
let each = function(...args: any[]): void {};
let getName = (name = '橙某人'): string => name;
🍊关于如何定义任何函数类型?
可以使 Function
类型。
javascript
let fn1: Function = () => {};
let fn2: Function = (a: string) => {};
let fn3: Function = function fn() {};
可选参数
在 JS
中,函数参数默认都是可选的,传不传取决于函数具体的业务逻辑。而在 TS
中,函数参数默认都是必填的,定义了参数但是不传就会有报错。
如果你想将参数变成可选的,需要使用 ?
符号来明确定义。
🍊关于如何用除了 ?
符号还能怎样将函数参数变成可选的?
可以使用默认值。
默认值
TS
函数的默认值与 JS
是一致的,但 TS
的默认值有两个好处。
- 函数默认值能自动推断参数类型。
- 函数默认值能将参数变成可选的。
还有,还有,还有一点要注意的❗函数默认值和可选参数不能同时使用。
rest
rest
参数的类型可以是数组或者元组类型,一样必须放到参数最后一位。
javascript
function fn1(a: number, ...args: number[]) {}
function fn2(a: number, ...args: [number, string, boolean]) {}
返回值
函数返回值的类型定义是直接在括号(():T
)加上类型即可。
javascript
function fn(): T {}
值得一提的是返回值类型也能自动进行类型推断。
函数重载
函数重载 是 TS
自己扩展的一个概念,注意 JS
是没有的。
我们先不去探究它的概念情况,来看一个例子。
"有一个数组,我们期待实现一个函数,该函数根据不同数量的参数对数组有不同行为的表现。"
在 JS
中的实现:
javascript
const target = [1, 2, 3];
function fn(...args) {
if (args.length === 0) {
// 没有参数的时候,直接打印
target.forEach(val => {
console.log(val);
})
} else if (args.length === 1) {
// 只有一个参数的时候,通过索引找到值
return target[args[0]];
}else {
// 多个餐护士的时候,直接返回数组
return target;
}
}
而改成 TS
形式,也仅需将参数和返回值类型加上即可,如下:
javascript
const target = [1, 2, 3];
function fn(...args: any[]): void | number | number[] {
if (args.length === 0) {
target.forEach(val => {
console.log(val);
})
} else if (args.length === 1) {
return target[args[0]];
}else {
return target;
}
}
不过,你以为如此就完了吗?那...当然,也是可以就这样就完了的。😇
不过铁子也可以继续听小编扯一扯。👻
此时,如果你去调用该函数,函数返回结果的类型永远都会是 number | void | number[]
。
javascript
const result1 = fn(); // number | void | number[]
const result2 = fn(1); // number | void | number[]
const result3 = fn(1, 2); // number | void | number[]
const result4 = fn(1, 2, 3); // number | void | number[]
然后呢❓❓❓这有什么问题吗❓
当然有,拿 result2
来说,其实我们能机构它的类型应该是 number
类型。此时,我们还将期待 result2
能通过 toFiexd()
方法,保留两位小数。
我们可能会如下调用 toFiexd()
方法:
但事与愿违,这样是不被允许。
而为了解决这种情况,就轮到"函数重载"隆重登场了,它规定必须给予函数完整的类型声明,才能准确得到期待的类型。
javascript
function fn(): void
function fn(index: number): number
function fn(...args: any[]): number[]
function fn(...args: any[]): void | number | number[] {
if (args.length === 0) {
target.forEach(val => {
console.log(val);
})
} else if (args.length === 1) {
return target[args[0]];
}else {
return target;
}
}
const result1 = fn(); // void
const result2 = fn(1).toFixed(2); // number
const result3 = fn(1, 2); // number[]
const result4 = fn(1, 2, 3); // number[]
这样你可明白它的作用?😋其实也没那么高大尚,不过学过 Java
的小伙伴可能对此更熟悉。
函数重载:指的是根据函数参数不同的类型或数量,执行不同逻辑的行为,称为函数重载(function overload)。
对象-Object
要声明一个对象类型,我们需要列出它的所有属性及类型。
javascript
let obj: { name: string, age: number } = { name: '橙某人', age: 18 };
一旦声明了类型,对象赋值时,就不能缺少指定的属性,也不能有多余的属性。
javascript
let obj1: { name: string, age: number } = { name: '橙某人' }; // ❌
let obj2: { name: string } = { name: '橙某人', age: 18 }; // ❌
但是,由于 TS
不区分对象自身的属性和继承的属性,一律视为对象的属性,所有,有些通过原型链隐式继承下来的属性,在赋值的时候,咱们也可以不用管。
比如:
javascript
let obj1: { name: string, toString: Function } = { name: '橙某人' }; // ✅
let obj2: { name: string, valueOf: Function } = { name: '橙某人' }; // ✅
可选参数
对象类型也可以通过 ?
符号指定部分或者全部属性是可选的。
javascript
let obj: { name: string, age?: number } = { name: '橙某人' };
解构赋值
对象解构赋值的类型定义有两种形式。
javascript
let { name, age }: { name: string, age: number } = { name: '橙某人', age: 18 };
// or
let { name, age } = { name: '橙某人', age: 18 }; // 类型推断
注意❗一定不能写成这样子:
javascript
let { name: string, age: number } = { name: '橙某人', age: 18 }; // ❌
这样表示多定义了两个变量 let string;
和 let number;
目前还无法为解构变量指定类型,因为对象解构里面的冒号,JS
指定了其他用途。
结构类型原则
先来看个例子:
javascript
let person: { name: string } = { name: '人类' };
let student: { name: string, age: number } = { name: '橙某人', age: 18 };
person = student; // ✅
student = person; // ❌
看这个结果是不是有点像包含、继承的意味?
只要 student
类型满足 person
类型的结构特征,TS
就会认为 student
类型是其子类型,person
是父类型。凡是可以使用父类型的地方,都可以使用子类型。
这种特性被称之为"结构类型"(structual typing)原则。
至此,本篇文章就写完啦,撒花撒花。
希望本文对你有所帮助,如有任何疑问,期待你的留言哦。
老样子,点赞+评论=你会了,收藏=你精通了。