TypeScript 笔记

一、编译

1. 命令行编译

  • 第一步: 创建.ts文件
  • 第二步: 全局安装TypeScript
    1
    npm install -g typescript
  • 第三步: 使用命令行编译
    1
    tsc example.ts

2. 自动化编译

  • 第一步: 创建TypeScript编译配置文件
    1
    tsc --init
  • 第二步: 监视目录的.ts变化
    1
    tsc --watch
  • 第三步: 小优化
    1
    tsc --noEmitOnError --watch

二、类型声明

1
2
3
4
5
6
7
let a: string = "abc";
let b: number = 91;
let c: boolean = true;

function count(x: number, y: number): number{
return x + y;
}

三、类型推断

1
2
let a = 123;
a = "abc"// 报错!!!

四、类型总览

1. JavaScript 中的数据类型

  • string
  • number
  • boolean
  • null
  • undefined
  • bigint
  • symbol
  • object

    object包含:ArrayFunctionDateError等…

2. JavaScript 中的数据类型

  1. 上述所有类型
  2. 六个新类型
    • `any
    • unknown
    • never
    • void
    • tuple
    • enum
  3. 两个用于自定义类型的方式
    • type
    • interface

3. 注意

JavaScriptTypeScript中内置构造函数:NumberStringBoolean可用于创建包装对象,日常开发少用,通常使用小写的类型

五、常用类型

1. any

1
2
3
4
5
6
7
8
9
10
// 显式any
let a: any;
a = 91;
a = "abc";
a = true;
// 隐式any
let b;
b = 91;
b = "abc";
b = true;

any类型不进行类型检查

1
2
3
let c = true;
let d: string = c;
console.log(d);// true;

any类型可赋值给任何类型

2. unknown

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
let a: unknown;
let b: string;

a = 91;
a = "abc";
a = true;

b= a;// 报错!!!

a = "abc";
b = a;// 报错!!!

// 第一种方式
if(typeof a === 'string'){
b = a;// 正常赋值
}

// 第二种方式(断言)
x = a as string;

// 第三种方式(断言)
x = <string>a;

unknownany相似,但是不能直接赋值给其他变量,即便类型是相同的

1
2
3
4
5
6
7
8
9
10
11
let a: string = 'hi';
a.toUpperCase();// 无警告

let a: any = 'hi';
a.toUpperCase();// 无警告

let a: unknown = 'hi';
a.toUpperCase();// 报错!!!

(a as string).toUpperCase();
<string>a.toUpperCase();

unknown调用任何属性方法都会报错,与any恰恰相反

3. never

1. 几乎不用never限制变量,没有意义

2. 可用于限制函数。返回never的函数不能有可访问的终节点

1
2
3
4
5
6
7
8
9
10
11
12
function test() : nerver {
}// 报错,函数没return,默认返回undefined类型

// 情况一
function test() : nerver {
test()
}// 函数无限运行,不会返回值,无警告

// 情况二
function test() : nerver {
throw new Error("异常");
}// 函数报错,直接结束调用,没有返回值,无警告

3. never一般为TypeScript主动推断出来的

1
2
3
4
5
6
7
let a: string = "abc";

if(a === 'string') {
console.log(a.toUpperCase());
}else{
console.log(a);// TypeScript推断此处a为never,因为没有任何一个值符合该情况
}

4. void

void通常用于函数返回值声明,含义:函数返回空(undefined),调用者也不应依赖返回值进行任何操作

1
2
3
4
5
function logMessage(msg: string) : void {
console.log(msg);
}// return除了undefined的类型都会报错(any因其独特的机制也可以返回)
// 三种返回方式:return undefined; return; (啥也不写,隐式返回undefined)
logMessage("hi");
1
2
3
4
5
6
7
8
9
10
11
12
13
function test1() : void {

}
let result1 = test1();
console.log(result1);// undefined
if(result1){
}// 报错,不应该用void1的返回值进行操作

function test2() : undefined {
}
let result2 = test2();
if(result2){
}// 无警告

5. object

1. objectObject项目不常用

  • object不能存原始类型(number, boolean, string, null, undefined)只能存非原始类型
  • Object能存储可以调用到Object方法的类型(除了nullundefined

2. 声明对象类型

1. 常用以下形式

1
2
3
4
5
6
7
// 三种方式,?表示可选属性
let Person = { name: string, age?: number }
let Person = { name: string; age?: number }
let Person = {
name: string
age?: number
}

2. 索引签名

1
2
3
4
5
6
7
// 三种方式,?表示可选属性
let Person = {
name: string
age?: number
[key: string]: any// 可以在对象上加任何属性,key改为任何变量名都没问题
}
Person = { name: "xxx", age: 20, city: "Beijin"}

3. 声明函数类型

1
2
3
4
5
let count: (a: number, b: number) => number

count = function (x, y) {
return x + y;
}

TypeScript中的=>在函数声明时表示函数类型,描述其参数类型返回类型
JavaScript中的=>表示箭头函数

4. 声明数组类型

1
2
3
4
5
let arr1: string[];
let arr2: Array<number>;

arr1 = ['a', 'b'];
arr2 = [12, 34];

6. tuple

元组是一种特殊的数组类型,可以存储固定数量的元素,并且每个元素的类型是已知的且可以不同,元组用于精确描述一组值的类型,?表示可选元素,...表示任意多个。

1
2
3
let a: [string, number];
let b: [string, boolean?];
let c: [string, ...string[]];

7. enum

1. 数字枚举

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
enum Direction {
Up,
Down,
Left,
Right
}

function walk(data: Direction) {
if(data === Direction.Up){
console.log("向上走");
}
if(data === Direction.Down){
console.log("向下走");
}
if(data === Direction.Left){
console.log("向左走");
}
if(data === Direction.Right){
console.log("向右走");
}
}

Walk(Direction.Up);

成员值自动递增,且数字还具备反向映射的特点。

2. 字符串枚举

1
2
3
4
5
6
7
8
9
enum Direction {
Up = "up",
Down = "down",
Left = "left",
Right = "right"
}

let dir: Direction = Direction.Up;
console.log(dir);// "UP"

3. 常量枚举

1
2
3
4
5
6
7
8
const enum Direction {
Up,
Down,
Left,
Right
}

console.log(Direction.Up);

常量枚举使用const关键字定义,在编译时会被内联,避免生成额外代码
编译时内联:
TypeScript在编译时,会枚举成员引用替换为它的实际值而不是生成额外的枚举对象。可以减少JavaScript代码量,提高运行效率。

8. type

1. 基本用法

1
2
3
type shuzi = number;

ket a: shuzi;

2. 联合类型

1
2
3
4
5
6
7
type Status = number | string;
type Gender = '男' | '女';

function printStatus(data: Status): void{
console.log(data)
}

3. 交叉类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
type Area = {
height: number
width: numer
};

type Address = {
num: number
cell: number
room: string
};

type House = Area & Address;

const house: House = {
height: 100,
weight: 100,
num: 1,
cel: 2,
room: '702'
}

9. 一种特殊情况

1
2
3
4
5
6
7
8
function demo(): void{
return undefined;
// 以下均不合法
return 100;
return false;
return null;
return [];
}
1
2
3
4
5
6
7
8
9
10
type LogFunc = () => void;

const f1: LogFunc = function() {
// 以下均合法
return undefined;
return 100;
return false;
return null;
return [];
};
1
2
3
4
const src = [1, 2, 3];
const dst = [0];

src.forEach((e1) => dst.push(e1))

forEach()的期望返回值是void,而push()的返回值是number。为了使类似的代码成立,而做了取舍

11. 类

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
class Person {
name: string;
age: number;
constructor(name: string, age: number){
this.name = name;
this.age = age;
}
speak() {
console.log(`名字:${this.name},年龄:${this.age}`);
}
}

class Student extends Person {
grade: string;
constructor(name: string, age: number, grade: string){
super(name, age);
this.grade = grade;
}
study(){
console.log(`${this.name},正在学习`);
}
override speak() {
console.log(`我的名字:${this.name},我的年龄:${this.age}`);
}
}

12. 属性修饰符

修饰符 含义 具体规则
public 公开的 可以被:类内部,子类,类外部访问
protected 受保护的 可以被:类内部,子类访问
private 私有的 可以被:类内部访问
readonly 只读属性 属性无法修改
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 简写前
class Person {
name: string;
age: number;
constructor(name: string, age: number){
this.name = name;
this.age = age;
} this.name = name;
this.age = age;
}
}
// 简写后
class Person {
constructor(public name: string, public age: number) {}
}

12. 抽象类

抽象类无法实例化,意义在于可以被继承,抽象类里可以有普通方法,也可以有抽象方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
abstract class Package {
// 构造方法
constructer(public weight: number) {}
// 抽象方法
abstract calculate(): number
// 具体方法
printPackage() {
console.log("xxxx");
}
}
class StandardPackage extends Package {
constructor(
weight: number,
public uniPrice: number
) { super(weight) }
calculate(): number{
return this.weight * this.unitPrice;
}
}

13. interface

接口为类,对象,函数等规定一种契约,确保代码一致性和类型安全。只能定义格式,不能包含任何实现

1. 定义类结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
interface PersonInterface {
name: string;
age: number;
speak(n: number): void
}

class Person implements PersonInterface {
constructor(
public name: string,
public age: number
) {}
speak(n: number): void {
for (let i = 0; i < n; i++) {
console.log('xxxx')
}
}
}

2. 定义对象结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
interface UserInterface {
name: string;
readonly gender: string;
age?: number;
run: (n: number) => void;
}
const user: UserInterface = {
name: "zhangsan",
gender: "男",
age: 18,
run(n) {
console.log("xxx")
}
}

3. 定义函数结构

1
2
3
4
5
6
7
interface CountInterface {
(a: number, b: number): number;
}

const count: CountInterface = (x, y) => {
return x + y
}

4. 接口的继承

1
2
3
4
5
6
7
8
9
10
11
12
13
interface PersonInterface {
name: string;
age: number;
}
interface StudentInterface extends PersonInterface {
grade: string;
}

const stu: StudentInterface = {
name: "xxx",
age: 18,
grade: "xxx"
}

5. 接口的自动合并

1
2
3
4
5
6
7
8
9
10
11
12
13
interface PersonInterface {
name: string;
age: number;
}
interface PersonInterface {
gender: string;
}

const p: PersonInterface = {
name: "xxx",
age: 18,
gender: "xxx"
}

14. 相似概念的区别

1. interfacetype的区别

  • 相同点:interfacetype都可以用于定义对象结构,两者在许多场景中是可以互换的。
    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
    interface PersonInterface {
    name: string;
    age: number;
    speak(): void;
    }
    type PersonType = {
    name: string;
    age: number;
    speak(): void;
    }

    let p1: PersonInterface = {
    name: "xxx",
    age: 18,
    speak(){
    console.log("xxx");
    }
    }
    let p2: PersonType = {
    name: "xxx",
    age: 18,
    speak(){
    console.log("xxx");
    }
    }
  • 不同点:
    1. interface :更专注于定义对象和类的结构,支持继承与合并。
    2. type:可以自定义类型别名,联合类型,交叉类型,但不支持继承和自动合并。

2. interface和抽象类的区别

  • 相同点:都用于定义一个类的格式
  • 不同点:
    1. interface:只能描述结构,不能有任何实现代码,一个类可以实现多个接口。
    2. 抽象类:既可以包括抽象方法,又可以包含具体方法,一个类只能继承一个抽象类。

六、泛型

泛型允许我们在定义函数,类或接口时,使用类型参数来表示未指定的类型,这些参数在具体使用时,才能被指定具体类型。泛型能让同一段代码适用于多种类型同时仍保证类型的安全性。

1. 泛型函数

1
2
3
4
5
6
function logData<T,U>(data1: T, data2: U): T | U {
return Data.now() % 2 ? data1 : data2;
}

console.log(logData<number, bool>(1000, true));
console.log(logData<string, number>('hello', 666));

2. 泛型接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
interface PersonInterface<T> {
name: string;
age: number;
extraInfo: T
}
type Home = {
address: string;
postalCode: number;
}
let p: PersonInterface<Home> = {
name: "TOM",
age: 18,
extraInfo: {
address: "xxxx",
postalCode: 123485
}
}

3. 泛型类

1
2
3
4
5
6
7
8
9
10
11
12
class Person<T> {
constructor(
public name: string,
public age: number,
public extraInfo: T
) {}
speak(){
console.log("xxxx");
}
}

const p1 = new Person<number>("xxx", 18, 250);

七、类型声明文件

.d.ts文件为现有的javascript代码提供类型信息

1
2
3
4
// demo.js
export function add(a, b){
return a + b;
}
1
2
3
// demo.d.ts
declare function add(a: number, b: number): number;
export(add)

八、装饰器