这是我参与「第五届青训营 」伴学笔记创作活动的第 8 天
一、什么是 TypeScript
1. TypeScript 的历史
- 2012-10 微软发布了 TypeScript 的第一个版本
- Angular、 React 和 Vue 3.0 都官方支持了 TypeScript
- Visual Studio Code 完美兼容 TypeScript
2. 为什么选择 TypeScript
-
TypeScript 是添加了类型系统的 JavaScript
-
TypeScript 是 JavaScript 的超集
-
完全兼容 JavaScript 特性,支持共存
-
支持渐进式引入与升级
-
-
TypeScript 是静态类型语言,可以在编译阶段提供类型检查
- 静态类型的优势
-
增强代码可读性:基于语法解析 TSDoc,IDE 可以提供增强
-
增强代码可维护性:在编译阶段暴露大部分错误,节省 debug 实现
-
动态类型在执行阶段匹配类型,静态类型在编译阶段匹配类型
关于动静类型和强弱类型的定义可参考:辨析编程语言的四种类型:动静类型与强弱类型
- 静态类型的优势
二、TypeScript 的语法
1. 基础数据类型
TypeScript 作为静态类型语言,其需要在定义变量时指定变量的类型
const q: string = "string"; //字符串
const w: number = 1; //数字
const e: boolean = true; //布尔值
const r: null = null; //null
const t: undefined = undefined; //undefined
使用
<varName>: <varType> = <value>来声明变量
2. 对象类型
2.1 定义接口
TypeScript 中使用接口(interface)来定义对象的类型和内容
interface IByteDance {
readonly jobId: number;
name: string;
sex: 'man' | 'woman' | 'other';
age: number;
hobby?: string;
[key: string]: any;
}
readonly:定义该属性为只读,即在对象初始化外之外不可被赋值es
sex: 'man' | 'woman' | 'other':定义sex属性,仅能为该三个选项之一
hobby?:定义hobby属性为可选属性,可以不定义该属性
[key: string]: any:任意属性,并约束所有对象属性都必须是该属性的子类型
any类型将会跳过编译时类型检查,等到运行时再确定具体的类型
2.2 定义对象
透过将对象类型指向接口来实现对象的类型和内容预定义
const byteDancer: IByteDance = {jobId: 123, name: "Bob", sex: "man", age: 15}
定义
byteDancer对象的类型为IByteDance未定义可选属性
hobby
2.3 修改对象
2.3.1 只读属性
byteDancer.jobId = 456;
TS2540: Cannot assign to 'jobId' because it is a read-only property.只读属性,无法修改
2.3.2 任意属性
byteDance.platform = "data";
任意属性标注下可以添加任意属性
2.3.3 缺少属性
const byteDancer2: IByteDance = {jobId: 123, sex: "man", age: 15}
TS2741: Property 'name' is missing in type '{ age: number; jobId: number; sex: "man"; }' but required in type 'IByteDance'.缺少属性
name,bobby可缺省
3.函数类型
TypeScript 中使用function funcName(): <returnType> { ... }来定义函数类型
3.1 带参数函数
function mult(x: number, y: number): string {
return (x * y).toString();
}
定义函数
mult,包含参数x和y,均为number类型,函数的返回值为string类型
3.2 可选参数
function buildName(firstName: string, lastName?: string) {
if (lastName) {
return firstName + " " + lastName;
}
else {
return firstName;
}
}
lastName?:可选参数lastName
3.3 默认参数
function calculate_discount(price: number, rate: number = 0.50) {
let discount = price * rate;
console.log("Result: ", discount);
return discount;
}
rate: number = 0.50:定义参数rate的类型为number,默认值为0.50
3.4 Lambda 函数(箭头函数)
3.4.1 基于 interface 实现
interface Imult {
(x: number, y: number): string;
}
const mult: Imult = (x, y) => (x * y).toString()
3.4.2 常规实现
const mult: (x: number, y: number) => string = (x, y) => (x * y).toString()
3.5 函数重载
用来实现不同参数输入对应不同参数输出的函数。
需要定义多个重载签名,一个实现签名,一个函数体构造。
let suits = ["hearts", "spades", "clubs", "diamonds"];
// 定义重载签名
function greet(person: string): string;
function greet(persons: string[]): string[];
// 定义实现签名
function greet(person: unknown): unknown {
if (typeof person === 'string') {
return `Hello, ${person}!`;
} else if (Array.isArray(person)) {
return person.map(name => `Hello, ${name}!`);
}
throw new Error('Unable to greet');
}
console.log(greet(suits[0]));
console.log(greet(suits));
参考资料
4. 数组类型
TypeScript 中通常使用类型[]或Array<>来表示一个数组
// 类型[] 表示
type IArr1 = number[];
// 泛型表示
type IArr2 = Array<string | number>;
// 元组表示
type IArr3 = [number, number, number, string];
// 接口表示
interface IArr4 {[key: number]: any;}
const arr1: IArr1 = [1, 2, 3]
const arr2: IArr2 = ["Bob", 123, "123"]
const arr3: IArr3 = [123, 123, "abc", "abc"]
const arr4: IArr4 = ["string", () => null, {}, []]
number[]:由数字组成的数组
Array<string | number>:使用Array关键字定义数组,由string或number类型组成
[number, number, string, string]:前两项为数字,后两项为字符串
5. 空类型
空类型,表示无赋值
type IEmptyFunction = () => void;
6. 任意类型
任意类型,所有类型的子类型
type IAnyType = any;
7. 枚举类型
枚举类型,对标准数组类型的补充,支持枚举值到枚举名的正、反向映射,可以手动为元素编号
enum EColor {Mon, Tue, Wed, Thu, Fri, Sat, Sun}
console.log(EColor['Tue']); //1
console.log(EColor[1]); //Tue
这将会把
Mon自动赋值为0,Sun自动赋值为6
enum EColor {Mon = 1, Tue, Wed, Thu, Fri, Sat, Sun}
这将会把
Mon赋值为1,Sun自动赋值为7
enum EColor {Mon = -2, Tue, Wed, Thu, Fri, Sat, Sun}
这将会把
Mon赋值为-2,Sun自动赋值为4
enum EColor {Mon = 2, Tue = 3, Wed = 5, Thu = 8}
console.log(EColor['Tue']); //3
console.log(EColor[5]); //Wed
也可以像这样对每一个元素手动赋值
8. 泛型
8.1 什么是泛型
泛型(Generics)是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性
8.2 使用泛型
function createArray<T>(length: number, value: T): Array<T> {
let result: T[] = [];
for (let i = 0; i < length; i++) {
result[i] = value;
}
return result;
}
createArray(3, 'x'); // ['x', 'x', 'x']
createArray(3, 20); // [20, 20, 20]
这个函数被用来创建一个数组,给定两个参数(填充数量,填充内容)
这里我们预期这个函数的
value参数应该接受一个未知类型T的值,返回一个仅包含类型为T的元素的数组
createArray<T>:定义这个函数将要接受一个未知类型T的参数
value: T:定义value参数的类型为T
Array<T>:定义元素类型为T的数组
8.3 多个类型参数
定义泛型的时候,可以一次定义多个类型参数
function swap<T, U>(tuple: [T, U]): [U, T] {
return [tuple[1], tuple[0]];
}
swap([7, 'seven']); // ['seven', 7]
8.4 泛型约束
由于在函数内使用泛型时,不知道其是那种类型,不能随意操作其属性或方法。
这时可以使用interface关键字定义接口,并使用extends关键字要求泛型T继承自Lengthwise,并基于此要求T必须包含length属性
interface Lengthwise {
length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
console.log(arg.length);
return arg;
}
T extends Lengthwise:T继承自Lengthwise,即要求T必须包含length属性
8.5 泛型参数的默认类型
在 TypeScript 2.3 以后,我们可以为泛型中的类型参数指定默认类型。当使用泛型时没有在代码中直接指定类型参数,从实际值参数中也无法推测出时,这个默认类型就会起作用
function createArray<T = string>(length: number, value: T): Array<T> {
let result: T[] = [];
for (let i = 0; i < length; i++) {
result[i] = value;
}
return result;
}
<T = string>:定义泛型T的默认类型为string
9. 类型别名
当需要重复使用一个较为复杂的类型时,可以使用type关键字来指定类型别名,即使用一个变量来指代一个复杂类型
type IObjArr = Array<{
key: string;
[objKey: string]: any;
}>
10. 类型断言
-
类型断言(Type Assertion)可以用来手动指定一个值的类型
-
一般使用
as关键字来指定类型断言 -
类型断言的作用主要是告诉编译器如何判断这是什么类型,这并不会体现在生成的JS中
TypeScript 代码:
let someValue: any = "this is a string";
let strLength: number = (someValue as string).length;
JavaScript 代码:
var someValue = "this is a string";
var strLength = someValue.length;
11 字面量(字符串/数字)
字面量用以指定字符串或数字所必须使用的固定值
type IDomTag = 'html' | 'body' | 'div' | 'span';
type IOddNumber = 1 | 3 | 5 | 7 | 9;
IDomTag必须为'html'、'body'、'div'、'span'其中之一
IOddNumber必须为1、3、5、7、9其中之一