TS
一、类型检测
0、type定义类型别名,方便多次复用
// type用于定义类型别名(type alias)
type IDType = string | number | boolean
type PointType = {
x: number
y: number
z?: number
}
function printId(id: IDType) {
}
function printPoint(point: PointType) {
}
1、number、string、boolean 类型
let name: string = "why";
let num1: number = 100;
let flag: boolean = true;
2、array类型
- array类型里只能放同一种类型,如:string[ ]代表数组里只能放string类型
const names1: Array<string> = []; // 不推荐
const names2: string[] = []; // 推荐
3、tuple类型
- tuple类型里可以放多种类型,但类型需要一一对应
const info: [string, number, number] = ["why", 18, 1.88]; // 类型需要 一一对应
const name = info[0];
console.log(name.length);
const age = info[1];
console.log(age.length); // 这里会报错,因为array类型无length方法
- 因为tuple里面可以放多个类型,所以比较适合作为函数的返回值
// [any, (newState: any) => void] 为返回值类型
function useState(state: any): [any, (newState: any) => void] {
let currentState = state;
const changeState = (newState: any) => {
console.log(newState);
};
return [currentState, changeState];
}
4、object类型
const info: {
name: string;
age: number;
} = {
name: "why",
age: 18,
};
console.log(info.name);
5、null 和 undefined
// null只能赋值null undefined只能赋值undefined
let n1: null = null;
let n2: undefined = undefined;
- unknown的使用
let flag = true;
let result: unknown; // 最好不要使用any
if (flag) {
result = 123;
} else {
result = "abc";
}
console.log(result);
6、Symbol类型
const title1 = Symbol("title");
const title2 = Symbol("title");
const info = {
[title1]: "程序员",
[title2]: "老师",
};
// 取值
console.log(info[title1]);
7、void类型
函数无返回值的类型就是void
// 默认就是void
function sum(num1: number, num2: number) {
console.log(num1 + num2);
}
8、never类型
never类型不匹配任何类型,所以赋任何类型的值都会报错
function handleMessage(message: string | number | boolean) {
switch (typeof message) {
case "string":
console.log("string处理方式处理message");
break;
case "number":
console.log("number处理方式处理message");
break;
// 防止给message增加类型后忘记编写相关代码
// 打开以下备注 就不会报错;
// case "boolean":
// console.log("boolean处理方式处理message");
// break;
default:
// never 类型不存在赋值过程 所以当未匹配的boolean类型的message赋值给check时会报错
const check: never = message;
}
}
handleMessage(true); //调用未匹配的boolean类型
9、any类型
在不想给某些JavaScript添加具体的数据类型时,可以使用any类型,便会跳过类型检测,和原生的JavaScript代码是一样的;
let message: any = "Hello World";
message = 123;
message = true;
message = {};
10、字面量类型
字面量类型 ,值和类型是保持一致的,常结合联合类型使用
type Method = "GET" | "POST"; // 这样Method只能为这两个值
type Request = {
url: string;
method: Method;
};
function request(url: string, method: Method) {}
const options: Request = {
url: "https://www.coderwhy.org/abc",
method: "POST",
};
11、函数相关的类型
- 函数的参数和返回值都需要确定类型,参数必须明确写出类型,返回值默认会自动推导;
function sum(num1: number, num2: number):number {
return num1 + num2
}
// 参数也可以为对象,函数,相应的需要确定类型
function printPoint(point: {x: number, y: number}) {
console.log(point.x);
console.log(point.y)
}
printPoint({x: 123, y: 321})
- 匿名函数的类型推导
const names = ["abc", "cba", "nba"]
// item根据上下文的环境推导出来的, 这个时候可以不添加的类型注解
// 上下文中的函数: 可以不添加类型注解
names.forEach(function(item) {
console.log(item.split(""))
})
- 对象类型也可以指定哪些属性是可选的,可以在属性的后面添加一个“?”
function printPoint(point: {x: number, y: number, z?: number}) {
console.log(point.x)
console.log(point.y)
console.log(point.z)
}
printPoint({x: 123, y: 321})
printPoint({x: 123, y: 321, z: 111})
- 联合类型
function printID(id: number | string | boolean) {
// 使用联合类型的值时, 需要特别的小心
// narrow: 缩小
if (typeof id === "string") {
// TypeScript帮助确定id一定是string类型
console.log(id.toUpperCase());
} else {
console.log(id);
}
}
printID(123);
printID("abc");
printID(true);
使用联合类型后因为一个参数可以取多个类型,所以需要先确定类型(即缩小类型),才能调用每个类型独有的方法与属性;
下面是类型缩小的几种方式:
// 1.typeof的类型缩小
type IDType = number | string;
function printID(id: IDType) {
if (typeof id === "string") {
console.log(id.toUpperCase());
} else {
console.log(id);
}
}
// 2.平等的类型缩小(=== == !== !=/switch)
type Direction = "left" | "right" | "top" | "bottom";
function printDirection(direction: Direction) {
// 1.if判断
// if (direction === 'left') {
// console.log(direction)
// } else if ()
// 2.switch判断
// switch (direction) {
// case 'left':
// console.log(direction)
// break;
// case ...
// }
}
// 3.instanceof
function printTime(time: string | Date) {
if (time instanceof Date) {
console.log(time.toUTCString());
} else {
console.log(time);
}
}
class Student {
studying() {}
}
class Teacher {
teaching() {}
}
function work(p: Student | Teacher) {
if (p instanceof Student) {
p.studying();
} else {
p.teaching();
}
}
const stu = new Student();
work(stu);
// 4. in
type Fish = {
swimming: () => void;
};
type Dog = {
running: () => void;
};
function walk(animal: Fish | Dog) {
if ("swimming" in animal) {
animal.swimming();
} else {
animal.running();
}
}
const fish: Fish = {
swimming() {
console.log("swimming");
},
// 不能加running方法
// running(){
// console.log("running")
// }
};
walk(fish);
12、类型断言(缩小类型范围)
- 使用DOM元素特有的属性和方法时
const el = document.getElementById("why") as HTMLImageElement;
el.src = "url地址";
- 使用子类特有的属性和方法时
class Person {}
class Student extends Person {
studying() {}
}
function action(p: Person) {
// p.studying() 会报错
(p as Student).studying(); // 只有学生才能调用studying
}
const stu = new Student();
action(stu);
二、各种操作符
1、! 非空断言操作符
x! 将从 x 值域中排除 null 和 undefined;
function myFunc(maybeString: string | undefined | null) {
// Type 'string | null | undefined' is not assignable to type 'string'.
// Type 'undefined' is not assignable to type 'string'.
const onlyString: string = maybeString; // Error
const ignoreUndefinedAndNull: string = maybeString!; // Ok
}
2、可选链"?."
遇到null或者undefined时可以立即停止表达式的运行
const val = a?.b;
// 编译之后
var val = a === null || a === void 0 ? void 0 : a.b;
如果a为null或者void 0,会直接返回void 0,而不会接着执行a.b;
因此,以后我们可以不需要编写形如
if( a && a.b){
}
这样的保护性代码。更好的写法是:
if( a?.b){
}
唯一需要注意的是,?. 与 && 运算符并不100%等价。&& 专门用于检测 falsy 值,比如空字符串、0、NaN、null 和 false 等。而 ?. 只会验证对象是否为 null 或 undefined,对于 0 或空字符串来说,并不会出现 “短路”;
3、空值合并运算符(??)
- 空值合并运算符(??)是一个逻辑运算符。当左侧操作数为 null 或 undefined 时,其返回右侧的操作数。否则返回左侧的操作数。
- 与逻辑或(||)操作符不同,逻辑或会在左操作数为 falsy 值时返回右侧操作数。也就是说,如果你使用 || 来为某些变量设置默认的值时,你可能会遇到意料之外的行为。比如为 falsy 值(’’、NaN 或 0)时。
const foo = null ?? 'default string';
console.log(foo); // 输出:"default string"
const baz = 0 ?? 42;
console.log(baz); // 输出:0
以上 TS 代码经过编译后,会生成以下 ES5 代码:
"use strict";
var _a, _b;
var foo = (_a = null) !== null && _a !== void0 ? _a : 'default string';
console.log(foo); // 输出:"default string"
var baz = (_b = 0) !== null && _b !== void0 ? _b : 42;
console.log(baz); // 输出:0
三、函数详解
1、函数的可选类型
type SubtractsFnType = (num1: number, num2?: number) => number
const subtracts: SubtractsFnType = (a1: number, a2?: number) => {
if (a2) {
return a1 - a2
} else {
return a1
}
}
console.log(subtracts(5));
console.log(subtracts(5, 1));
export{}
2、函数的默认值
// 必传参数 - 有默认值的参数 - 可选参数
function foo(y: number, x: number = 20) {
console.log(x, y)
}
foo(30)
export{}
3、函数的剩余参数
function sum(initalNum: number, ...nums: number[]) {
let total = initalNum
for (const num of nums) {
total += num
}
return total
}
console.log(sum(20, 30))
console.log(sum(20, 30, 40))
console.log(sum(20, 30, 40, 50))
4、this相关
- this的默认推导
// this是可以被推导出来 info对象(TypeScript推导出来)
const info = {
name: "why",
eating() {
console.log(this.name + " eating")
}
}
info.eating()
export {}
- this的不明确类型
type ThisType = { name: string };
function eating(this: ThisType, message: string) {
console.log(this.name + " eating", message);
}
const info = {
name: "why",
eating: eating,
};
// 隐式绑定 info
info.eating("哈哈哈");
// 显示绑定
eating.call({name: "kobe"}, "呵呵呵")
eating.apply({name: "james"}, ["嘿嘿嘿"])
5、函数重载
在TypeScript中,如果我们编写了一个add函数,希望可以对字符串和数字类型进行相加,应该如何编写呢?
- 通过类型缩小
function add(a1: number | string, a2: number | string) {
if (typeof a1 === "number" && typeof a2 === "number") {
return a1 + a2
} else if (typeof a1 === "string" && typeof a2 === "string") {
return a1 + a2
}
// return a1 + a2;
}
add(10, 20)
- 通过函数重载
// 函数的重载: 函数的名称相同, 但是参数不同的几个函数, 就是函数的重载
/* 1.函数类型的声明 */
function add(num1: number, num2: number): number; // 没函数体
function add(num1: string, num2: string): string;
/* 2.函数体的实现 不能直接被调用 必须符合上面声明的类型才行 */
function add(num1: any, num2: any): any {
if (typeof num1 === "string" && typeof num2 === "string") {
return num1.length + num2.length;
}
return num1 + num2;
}
const result = add(20, 30);
const result2 = add("abc", "cba");
console.log(result);
console.log(result2);
// 必须和重载函数匹配 不然会报错
// add({name: "why"}, {age: 18})
export {};