TS笔记

本文最后更新于:2021年9月1日 凌晨

TS基础知识

官方提供了的在线开发 TypeScript 的云环境——Playground

TS环境搭建

  • Node — 从官网下载安装;
  • TypeScript — npm install -g typescript

TS版本查看

tsc -v

TS编译JS文件

 tsc xxx.js

TS配置文件

tsx --init,生成 tsconfig.json

TS降级编译

1
2
3
// 可以修改参数,设置编辑结果满足条件;
// 默认是 es3, 主流的一般是ES2015/es6,现在tsconfig中的target默认是ES2016/es7版本了
"target": "es5",

基础数据类型

1
2
3
4
5
6
7
8
let str: string = "jimmy";
let num: number = 24;
let bool: boolean = false;
let obj: object = {x: 1};
let u: undefined = undefined;
let n: null = null;
let big: bigint = 100n;
let sym: symbol = Symbol("me");

基础类型的大小写

初学 TypeScript 时,很容易和原始类型 number、string、boolean、symbol 混淆的首字母大写的 Number、String、Boolean、Symbol 类型,后者是相应原始类型的包装对象,姑且把它们称之为对象类型。

从类型兼容性上看,原始类型兼容对应的对象类型,反过来对象类型不兼容对应的原始类型。

1
2
3
4
5
6
7
let num: number;
let Num: Number;
Num = num; // ok
/*
TS2322: Type 'Number' is not assignable to type 'number'.   'number' is a primitive, but 'Number' is a // wrapper object. Prefer using 'number' when possible
*/
num = Num;

null 和 undefined

null 和 undefined 可以作为其他类型的子类;但是当我们如果在tsconfig.json文件中设置属性"strictNullChecks"为true,那么就不能设置其他类型的值为null / undefined

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// null和undefined赋值给string
let str:string = "666";
str = null
str= undefined

// null和undefined赋值给number
let num:number = 666;
num = null
num= undefined

// null和undefined赋值给object
let obj:object ={};
obj = null
obj= undefined

// null和undefined赋值给Symbol
let sym: symbol = Symbol("me");
sym = null
sym= undefined

// null和undefined赋值给boolean
let isDone: boolean = false;
isDone = null
isDone= undefined

// null和undefined赋值给bigint
let big: bigint = 100n;
big = null
big= undefined

number和bigint

即使number & bigint都表示的数字,但是这两个不兼容不能互相替换;

1
2
3
4
5
6
let big: bigint =  100n;
let num: number = 6;
big = num;
num = big;
// 会抛出一个类型不兼容的 ts(2322) 错误。
// 注意: bigint是ES2020的

any

在 TypeScript 中,任何类型都可以被归为 any 类型。这让 any 类型成为了类型系统的顶级类型:

希望请尽量别使用any,”Litchi”;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 如果定义成any, 则允许被赋值为任意类型。
let a: any = 666;
a = "Semlinker";
a = false;
a = 66
a = undefined
a = null
a = []
a = {}
// 在any上访问任何属性都是允许的,也允许调用任何方法.
let anyThing: any = 'hello';
console.log(anyThing.myName);
console.log(anyThing.myName.firstName);
let anyThing: any = 'Tom';
anyThing.setName('Jerry');
anyThing.setName('Jerry').sayHello();
anyThing.myName.setFirstName('Cat');

unknown

unknownany一样,所有类型都可以分配给unknown,unknownany的最大区别是: 任何类型的值可以赋值给any,同时any类型的值也可以赋值给任何类型。unknown 任何类型的值都可以赋值给它,但它只能赋值给unknownany;

1
2
3
4
5
6
7
8
let notSure: unknown = 4;
let uncertain: any = notSure; // OK

let notSure: any = 4;
let uncertain: unknown = notSure; // OK

let notSure: unknown = 4;
let uncertain: number = notSure; // Error

void

void表示没有任何类型,和其他类型是平等关系,不能直接赋值:

1
2
let a: void; 
let b: number = a; // Error

值得注意的是,方法没有返回值将得到undefined,但是我们需要定义成void类型,而不是undefined类型。否则将报错:

1
2
3
4
function fun(): undefined {
console.log("this is TypeScript");
};
fun(); // Error

never

never类型表示的是那些永不存在的值的类型,never类型同nullundefined一样,也是任何类型的子类型,也可以赋值给任何类型:

  • 如果一个函数执行时抛出了异常,那么这个函数永远不存在返回值(因为抛出异常会直接中断程序运行,这使得程序运行不到返回值那一步,即具有不可达的终点,也就永不存在返回了);
  • 函数中执行无限循环的代码(死循环),使得程序永远无法运行到函数返回值那一步,永不存在返回。

数组

定义数组的两个方式:

1
2
let arr: Array<string> = ["1", "2", "3"];
let copyArr: string[] = ["1", "2", "3"];

定义联合型数组:

1
2
let arr: Array<string | number> = [ 1, 2 , 3, "1", "2", "3" ];
let copyArr: (string | number)[] = [ 1, 2 , 3, "1", "2", "3" ];

对象数组:

1
2
3
4
5
6
// interface是接口,具体请看 interface
interface Arrobj{
name:string,
age:number
}
let arr3:Arrobj[]=[{name:'jimmy',age:22}]

函数

函数定义

1
2
3
4
// 参数的类型以及返回值的类型
function sum(x: number, y: number): number {
return x + y;
}

函数表达式

1
2
3
4
5
6
7
let mySum: (x: number, y: number) => number = function (x: number, y: number): number {
return x + y;
};
// 或者
let mySum = function (x: number, y: number): number {
return x + y;
};

可选参数

1
2
3
4
5
6
7
8
9
10
// 使用参数后面的 ?  表示该参数可选,可选参数后面不允许出现必须的参数,可选参数需要放在最后
function buildName(firstName: string, lastName?: string) {
if (lastName) {
return firstName + ' ' + lastName;
} else {
return firstName;
}
}
let tomcat = buildName('Tom', 'Cat');
let tom = buildName('Tom');

默认值类型

1
2
3
4
5
function buildName(firstName: string, lastName: string = 'Cat') {
return firstName + ' ' + lastName;
}
let tomcat = buildName('Tom', 'Cat');
let tom = buildName('Tom');

剩余参数

1
2
3
4
5
6
7
8
// 剩余参数是个数组
function push(array: any[], ...items: any[]) {
items.forEach(function(item) {
array.push(item);
});
}
let a = [];
push(a, 1, 2, 3);

元组(Tuple)

元组最重要的特性是可以限制 数组元素的个数和类型;

1
2
3
4
5
let x: [string, number];  // 类型必须匹配且个数必须为2

x = ['hello', 10]; // OK
x = ['hello', 10,10]; // Error
x = [10, 'hello']; // Error

元组的可选

1
2
3
4
5
let optionalTuple: [string, boolean?];
optionalTuple = ["Semlinker", true];
console.log(`optionalTuple : ${optionalTuple}`);
optionalTuple = ["Kakuqo"];
console.log(`optionalTuple : ${optionalTuple}`);

元组的剩余元素

1
2
3
4
5
// 元组只是一个比较特殊的数组,所以剩余元素也是数组
type RestTupleType = [number, ...string[]];
let restTuple: RestTupleType = [666, "Semlinker", "Kakuqo", "Lolo"];
console.log(restTuple[0]);
console.log(restTuple[1]);

元组的只读类型

TypeScript 3.4 还引入了对只读元组的新支持。我们可以为任何元组类型加上 readonly 关键字前缀,以使其成为只读元组。

1
const point: readonly [number, number] = [10, 20];

object,Object 和 { }

{}、大 Object 是比小 object 更宽泛的类型(least specific),{} 和大 Object 可以互相代替,用来表示原始类型(null、undefined 除外)和非原始类型;而小 object 则表示非原始类型。

object

1
2
3
4
5
6
7
let lowerCaseObject: object;
lowerCaseObject = 1; // ts(2322)
lowerCaseObject = 'a'; // ts(2322)
lowerCaseObject = true; // ts(2322)
lowerCaseObject = null; // ts(2322)
lowerCaseObject = undefined; // ts(2322)
lowerCaseObject = {}; // ok

Object

1
2
3
4
5
6
7
let upperCaseObject: Object;
upperCaseObject = 1; // ok
upperCaseObject = 'a'; // ok
upperCaseObject = true; // ok
upperCaseObject = null; // ts(2322)
upperCaseObject = undefined; // ts(2322)
upperCaseObject = {}; // ok

类型断言

告诉 TypeScript 按照我们的方式做类型检查。

语法:

1
2
3
4
5
6
7
8
// 尖括号 语法
let someValue: any = "this is a string";
let strLength: number = (<string>someValue).length;

// as 语法
let someValue: any = "this is a string";
let strLength: number = (someValue as string).length;
// 以上两种方式虽然没有任何区别,但是尖括号格式会与react中JSX产生语法冲突,因此我们更推荐使用 as 语法。

非空断言

1
2
3
4
5
6
7
8
type NumGenerator = () => number;

function myFunc(numGenerator: NumGenerator | undefined) {
// Object is possibly 'undefined'.(2532)
// Cannot invoke an object which is possibly 'undefined'.(2722)
const num1 = numGenerator(); // Error
const num2 = numGenerator!(); //OK
}

确定赋值断言

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
let x: number;
initialize();

// 正常使用报错,在使用前没有被赋值
// Variable 'x' is used before being assigned.(2454)
console.log(2 * x); // Error
function initialize() {
x = 10;
}


// 加上确定赋值断言,告诉他会被赋值
let x!: number;
initialize();
console.log(2 * x); // Ok

function initialize() {
x = 10;
}

字面量类型

目前,TypeScript 支持 3 种字面量类型:字符串字面量类型、数字字面量类型、布尔字面量类型:

1
2
3
type Direction = 'up' | 'down';
type Direction = 1 | 2;
type Direction = true | false;

交叉类型

1
2
3
4
5
6
7
type IntersectionType = { id: number; name: string; } & { age: number };
const mixed: IntersectionType = {
id: 1,
name: 'name',
age: 18
}
// IntersectionType 表示同时有id, name和age三个属性;从而实现等同接口继承的效果

类型(type)

给已有类型取别名 和 定义一个新的类型 ( 搭配联合类型使用 )

语法:type 别名 = 类型;

1
2
3
4
5
6
7
8
type StringType = string // 定义

let str1:StringType = 'abc'
let str2:StringType = 'abc'

type NewType = string | number // 定义类型
let a: NewType = 1
let b: NewType = '1'

接口(interface)

在 TypeScript 中,我们使用接口(Interfaces)来定义对象的类型。

1
2
3
4
5
6
7
8
interface Person {
name: string;
age: number;
}
let tom: Person = {
name: 'Tom',
age: 25
};

可选与只读

1
2
3
4
interface Person {
readonly name: string; // 只读
age?: number; // 可选
}

任意属性

定义了任意属性就不能定义可选属性,可选是任意的子集

1
2
3
4
5
6
7
8
9
interface Person {
name: string;
age?: number;
[propName: string]: any;
}
let tom: Person = {
name: 'Tom',
gender: 'male'
};

接口和类型区别

Other Type

类型别名可以用于其他类型,如基本类型(原始值)、联合类型、元组;但是接口只能定义对象;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// primitive
type Name = string;

// object
type PartialPointX = { x: number; };
type PartialPointY = { y: number; };

// union
type PartialPoint = PartialPointX | PartialPointY;

// tuple
type Data = [number, string];

// dom
let div = document.createElement('div');
type B = typeof div;

接口可以定义多次,类型别名不可以

1
2
3
interface Point { x: number; }
interface Point { y: number; }
const point: Point = { x: 1, y: 2 };

扩展

接口扩展接口

1
2
3
4
5
6
7
interface PointX {
x: number
}

interface Point extends PointX {
y: number
}

类型扩展类型(交叉类型)

1
2
3
4
5
6
7
type PointX = {
x: number
}

type Point = PointX & {
y: number
}

接口扩展类型

1
2
3
4
5
6
type PointX = {
x: number
}
interface Point extends PointX {
y: number
}

类型扩展接口

1
2
3
4
5
6
interface PointX {
x: number
}
type Point = PointX & {
y: number
}

泛型

语法:

1
2
3
4
5
// 这里的T就相当于参数一样传递,参数沿用传入的类型
function identity<T>(arg: T): T {
return arg;
}
identity<string>("1")

其中 T 代表 Type,在定义泛型时通常用作第一个类型变量名称。但实际上 T 可以用任何有效名称代替。除了 T 之外,以下是常见泛型变量代表的意思:

  • K(Key):表示对象中的键类型;
  • V(Value):表示对象中的值类型;
  • E(Element):表示元素类型。
1
2
3
4
5
6
// 定义多个
function identity <T, U>(value: T, message: U) : T {
console.log(message);
return value;
}
console.log(identity<Number, string>(68, "Semlinker"));

泛型约束

1
2
3
4
5
6
7
interface Sizeable {
size: number;
}
function trace<T extends Sizeable>(arg: T): T {
console.log(arg.size);
return arg;
}

TS笔记
http://www.clearluv.com/2021/09/01/TS笔记/
发布于
2021年9月1日
许可协议