文章目录
TypeScript里的类型注解是一种轻量级的为函数或变量添加类型约束的方式。
TypeScript提供了静态的代码分析,它可以分析代码的结构和提供的类型注解。
TypeScript的类型系统可以显示标记出代码中的意外行为,从而降低发生错误的可能性。
TypeScript数据类型大致可以分为两类:JS原有数据类型和TS新增数据类型。
JS原有数据类型
原始数据类型:number、string、boolean、null、undefined、symbol
引用数据类型:数组、对象、函数等
1.数值类型(number)
let num: number = 1234;
num = 5678;
console.log(num); // 5678
2.字符串类型(string)
let str: string = 'hello';
str = 'world'
console.log(str); // world
3.布尔类型(boolean)
let bool: boolean = true;
bool = false;
console.log(bool); // false
4.null
let aaa: null = null;
console.log(aaa); // null
5.undefined
let bbb: undefined = undefined;
console.log(bbb); // undefined
6.symbol
let ccc: symbol = Symbol('abc');
ccc = Symbol('123');
console.log(ccc); // Symbol(123)
7.数组类型(array)
// 方式一
let arr0: number[] = [1, 2, 3, 4];
let arr1: string[] = ['苹果', '樱桃', '西瓜'];
// 方式二 泛型写法
let arr2: Array<number> = [30, 40, 50];
let arr3: Array<string> = ['北京', '上海', '郑州'];
console.log(arr0); // [1, 2, 3, 4]
console.log(arr1); // ['苹果', '樱桃', '西瓜']
console.log(arr2); // [30, 40, 50]
console.log(arr3); // ['北京', '上海', '郑州']
【联合类型】
由两个或多个其他类型组成的类型,多个类型间使用竖线间隔,表示可以是这些类型中的任意一种。
let str0: number | boolean = true; // str0的值可以是数字或布尔类型
// 数组中既有 number 类型,又有 boolean 类型。
let arr4: (number | boolean)[] = [1, 2, true, false];
console.log(arr4); // [1, 2, true, false]
【自定义类型】
场景:某一类型(复杂)需要多次使用。
(创建类型别名后,直接使用该类型别名作为变量的类型注解即可)
type CustomArr = (number | string)[];
let arr5: CustomArr = [1, 2, 3, 'hello'];
let arr6: CustomArr = [1, 2, 'hello', 'world'];
console.log(arr5); // [1, 2, 3, 'hello']
console.log(arr6); // [1, 2, 'hello', 'world']
【多维数组】
TypeScript 使用 Type[][] 的形式,表示二维数组,Type 是最底层数组成员的类型。
let arr: number[][] = [[1, 2, 3, 4], [5, 6, 7], [8, 9]];
8.函数类型(function)
函数类型实际上,是指定函数参数和返回值的类型。(必须有返回值)
// 方式一
function add1(num1: number, num2: number): number {
return num1 + num2;
};
console.log(add1(1, 2)); // 3
// 方式二(箭头函数)
const add2 = (num1: number, num2: number): number => {
return num1 + num2;
};
console.log(add1(2, 3)); // 5
// 方式三
//(写法一)
const add3: (num1: number, num2: number) => number = (n1, n2) => {
return n1 + n2;
};
console.log(add3(3, 4)); // 7
//(写法二创建类型别名)
type CustomFun = (num1: number, num2: number) => number;
const add4: CustomFun = (n1, n2) => {
return n1 + n2;
}
console.log(add4(4, 5)); // 9
【函数void类型】
如果函数没有返回值,则函数返回值类型可设为:void
// void类型
function foo(): void {
// 不能有return
console.log('foo被执行了');
}
foo();
【函数可选参数】
? :表示可选参数,在可选参数的参数名称后面添加 ?
(可选参数只能出现在参数列表的最后,也就是说可选参数后面不能再出现必选参数)
// 可选参数
function add5(num: number, num1?: number): number {
if (num1) {
return num + num1
} else {
return num
}
}
console.log(add5(5)); // 5
console.log(add5(5, 6)); // 11
【函数剩余参数】
...变量名:表示剩余参数
// 剩余参数
function getInfo(str1: string, ...str2: string[]): void {
console.log(str1); // a
console.log(str2); // ['b', 'c', 'd']
}
getInfo('a', 'b', 'c', 'd');
9.对象类型(object)
对象结构使用 {} 来描述:{属性名: 类型的形式;方法名(): 返回值类型的形式}
如果方法有参数,像定义函数类型一样指定参数类型即可:getName(name: string): void
在一行代码中指定多个属性类型时,使用 ;(分号)来分隔多个属性类型
在一行代码只指定一个属性类型时,通过换行来分隔多个属性类型(可以去掉 ; )
对象结构中定义的属性和方法约束应均有实现,不能多也不能少
// 一行多个属性
let person1: { name: string; age: number; hobby(): void; } = {
name: '刘六',
age: 18,
hobby() {
console.log('学习');
}
}
person1.hobby(); // 学习
// 一行一个属性
let person2: {
name: string
age: number
hobby(): void
} = {
name: '王五',
age: 20,
hobby() {
console.log('学习');
}
}
console.log(person2.name); // 王五
console.log(person2.age); // 20
【对象可选属性】
场景:在使用 axios({ ... }) 时,如果发送 GET 请求,则method 属性可以省略。
function Axios(config: { url: string; method?: string }) {
console.log(config);
}
Axios({ url: '/api', method: 'POST' }); // {url: '/api', method: 'POST'}
Axios({ url: '/api' }); // {url: '/api'}
TS新增数据类型
新增数据类型:联合类型、自定义类型(类型别名)、void、any、元组、枚举、字面量类型、接口等。
1.联合类型
JS原有数据类型--数组类型--【联合类型】
2.自定义类型(类型别名)
JS原有数据类型--数组类型--【自定义类型】
3.void类型
JS原有数据类型--函数类型--【void类型】
4.any类型
当值为any类型时,可以对该值赋任何类型的值,进行任意操作。
变量类型一旦设为any,TypeScript 实际上会关闭这个变量的类型检查,失去TS类型保护的优势,即使可能存在错误,也并不会有代码提示。因此不推荐使用any。
【类型保护】
类型保护会在运行时检查变量以确保 TS 变量在特定的块级作用域内属于特定的类型,类型保护的主要思想是尝试检测属性、方法或原型,以确定如何处理值。
隐式具有 any 类型的情况:1.声明变量不提供类型也不提供默认值 2.函数参数不加类型。
// 赋any类型可以做任何操作
let obj: any = { x: 0 };
// 访问不存在的属性 或者 给属性赋值
console.log(obj.aaa); // undefined
obj.aaa = 10; // 赋值
// 对obj赋任何类型的值
obj = "aaa";
console.log(obj); // aaa
// 当作函数调用
obj(); // obj is not a function
// 赋值给其他类型的变量
let n: number = obj;
console.log(n); // {x: 0, aaa: 10}
// 隐式any类型
let a;
a = 'abc';
console.log(a);
a = 123;
console.log(a);
【污染】
类型除了关闭类型检查,还有一个很大的问题,就是它会“污染”其他变量。它可以赋值给其他任何类型的变量(因为没有类型检查),导致其他变量出错。这种错误要在运行时才能报错。
let x:any = 'hello';
let y:number;
y = x; // 不报错
y * 123 // 不报错
y.toFixed() // 不报错
5.unknown类型
为了解决any类型“污染”其他变量的问题,TypeScript 3.0 引入了unknown类型。一般来说,凡是需要设为any类型的地方,通常都应该优先考虑设为unknown类型。
与any类型相似,
所有类型的值都可以分配给unknown类型。
let a: unknown;
a = "a"; // 正确
a = true; // 正确
a = { x: 1, y: 2 }; // 正确
与any类型不同,
unknown类型变量不能直接赋值给其他类型的变量(除了any类型和unknown类型)。赋值给any和unknown以外类型的变量都会报错,这就避免了污染问题。
let b:unknown = 123;
let b1:boolean = v; // 报错
let b2:number = v; // 报错
unknown类型变量不能直接调用其方法和属性。
let c1:unknown = { foo: 123 };
console.log(c1.foo); // 报错
let c2:unknown = 'hello';
c2.trim(); // 报错
unknown类型变量能够进行的运算是有限的,只能进行比较运算(运算符==、===、!=、!==、||、&&、?)、取反运算( ! )、typeof运算符和instanceof运算符这几种,其他运算都会报错。需要经过“类型缩小”,unknown类型变量才可以使用。所谓“类型缩小”,就是缩小unknown变量的类型范围,确保不会出错。
let d: unknown = 1;
d += 1; // 报错
if (typeof d === 'number') {
d += 1; // 正确
}
let s: unknown = 'hello';
console.log(s.length); // 报错
if (typeof s === 'string') {
console.log(s.length); // 正确
}
6.never类型
为了保持与集合论的对应关系,以及类型运算的完整性,TypeScript 还引入了“空类型”的概念,即该类型为空,不包含任何值。类型为never,则不能赋给它任何值,否则都会报错。
let e: never;
e = false; // 报错
场景:当一个变量可能有多种类型(即联合类型),通常需要使用分支处理每一种类型。这时,处理所有可能的类型之后,剩余的情况就属于never类型。
function fn(x:string|number) {
if (typeof x === 'string') {
// ...
} else if (typeof x === 'number') {
// ...
} else {
x; // never 类型
}
}
7.元组类型
解决问题:(arr:number[])如此设置数组的类型注解,此数组中可以出现任意多个数字。
元组类型是另一种类型的数组,元组类型可以确切地标记出有多少个元素,以及每个元素的类型。
let arr: [number, string, boolean] = [12, 'abc', true];
console.log(arr); // [12, 'abc', true]
场景:使用经纬度坐标来标记位置信息。
let position: [number, number] = [39.3425, 117.2378];
console.log(position); // [39.3425, 117.2378]
8.字面量类型
任意的 JS 字面量(字符串、数字等)都可以作为类型使用。
优点:字面量类型更加精确、严谨。
const str1: 'Hello' = 'Hello'; // str1的值只能是字符串Hello
let age: 18 = 18; // age的值只能是数字18
场景:用来表示一组明确的可选值列表(字面量类型往往配合联合类型一起使用)
function changeDirection(direction: 'up' | 'down' | 'left' | 'right') { };
changeDirection('left'); // 参数direction的值只能是up/down/left/right中的任意一个。
9.枚举类型(enum)
枚举类型enum:定义一组明确的可选值。不过一般使用(字面量类型+联合类型组合)的方式定义可选值列表。
其他类型仅仅被当做类型,而枚举不仅用作类型,还提供值(枚举成员都是有值的)。
注意:1.约定枚举名称、枚举中的值以大写字母开头。2.枚举中的多个值之间通过 , (逗号)分隔。3.定义好枚举后,直接使用枚举名称作为类型注解。4.直接通过点 . (点)访问枚举成员。
// demo.ts
enum Sex{
Man,
Woman,
Secrecy
};
function changeSex(sex: Sex) {};
changeSex(Sex.Man); // 将枚举成员做实参使用
其他的类型会在编译为 JS 代码时自动移除,但是,枚举类型会被编译为 JS 代码。
// demo.js
var Sex;
(function (Sex) {
Sex["Man"] = "MAN";
Sex["Woman"] = "WOMAN";
Sex["Secrecy"] = "SECRECY";
})(Sex || (Sex = {})); // 若有值即为Sex,没有值设为{}
解释:先声明一个变量Sex,然后使用自执行函数将Sex设为对象并向Sex对象中添加属性。
【数值枚举】
枚举成员若未设置值,默认为:从0开始自增的数值。
enum Elem {
A,
B,
C
}
console.log(Elem.A, Elem.B, Elem.C); // 0 1 2
可以从第任意个成员设置初始值,其后未设置值的成员的值会默认自增。
enum Elem {
A,
B = 10,
C,
D = 20,
E
}
console.log(Elem.A, Elem.B, Elem.C, Elem.D, Elem.E); // 0 10 11 20 21
【字符串枚举】
字符串枚举没有自增行为,因此,字符串枚举的每个成员均必须有初始值。
enum Sex {
Man='MAN',
Woman='WOMAN',
Secrecy='SECRECY'
};
console.log(Sex.Man, Sex.Woman, Sex.Secrecy); // MAN WOMAN SECRECY
typeof 操作符
众所周知,JS 中提供了 typeof 操作符,用来获取数据的类型。TS 也提供了 typeof 操作符:可以在类型上下文中引用变量或属性的类型(类型查询)作为类型注解,以此来简化类型书写。
注意:typeof 只能用来查询变量或属性的类型。
let p: { x: number; y: number } = { x: 1, y: 2 };
function changeP(point: typeof p) {
// 等价于 function changeP(point: { x: number; y: number }) {}
console.log(point); // {x: 50, y: 100}
};
changeP({ x: 50, y: 100 })
/* ================================================== */
function add(num1: number, num2: number): number {
return num1 + num2
}
let reAdd: typeof add = (n1, n2) => {
// 等价于 let reAdd: (num1: number, num2: number) => number
return n1 + n2
}
reAdd("a",1); // 错误
console.log(reAdd(2, 3)); // 5