TypeScript
ts 是 js 的超集,它是一种基于类的面向对象编程语言,支持类型注解,可以编译成纯 JavaScript 代码。
为了解决 js 的一些问题, 如:
-
不清不楚的数据类型
-
错误逻辑不能提醒
-
访问未定义的变量
-
拼写错误
ts 的核心是静态类型检查
类型声明
typescript
let age: number = 25;
let name: string = 'John';
let isMarried: boolean = true;
cosnt add = (x: number, y: number): number =>{
return x + y;
}
console.log(age + '\n'); // 25
console.log(name + '\n'); // John
console.log(isMarried + '\n'); // true
console.log(add(2, 3) + '\n'); // 5
// age = '25'; // 报错
类型推断
typescript
let age = 25;
// 自动类型推断
// age = '25'; // 报错
类型总览
较 js, 新增了:
- any
- void
- unkonwn
- never
- tuple
- enum
自定义类型:
- type
- interface
注意:
typescript
let str1: string;
let str2: String;
str1 = "hello";
str2 = "world";
str2 = new String("world");
// str1 = new String("hello"); // 报错
常用类型
any
一旦设置为 any, 则不进行类型检查。
有显示和隐式两种方式。
any 类型的变量可以赋值给任何其他类型的变量
unknown
typescript
let value: unknown;
value = "hello";
value = 123;
value = true;
unknown 类型的变量不能赋值给任何其他类型的变量
typescript
let a: unknown;
a = "world";
let b: string = "hello";
// 使用断言
b = a as string;
b = <string>a; // node.js 不能识别
可以把 unkonw 看作保守的 any
never
限制函数的返回值类型,永远不会返回值。
函数要么异常结束, 要不永远不会结束:
typescript
function error(message: string): never {
throw new Error(message);
}
function loop(): never {
loop();
}
never 是 ts 可以主动推断出来的类型, 例如:
typescript
let a: string = "hello";
if (typeof a === "string") {
console.log(a.length);
} else {
console.log(a); // a 的类型被推断为 never
}
void
调用者不应该对其返回值做任何处理, 这是与 : undefined 的区别
typescript
function log(message: string): void {
console.log(message);
}
log("hello"); // 隐式返回undefined which is a kind of void
object
object 类型表示非原始类型, 因为范围较大, 实际应用较少。
Object
能够调用 Object 方法的类型
除了 null 和 undefined, 其他类型都可以作为 Object 类型。
声明对象类型
为了解决 object 和 Object 的问题:
typescript
let Person: {
name: string; // ; , \n 均可
age: number;
gender?: string; // ? 表示可选属性
};
person = {
name: "John",
age: 25,
gender: "male",
};
如果还有更多的属性要添加, 可以使用索引签名:
typescript
let person: {
name: string;
age: number;
[key: string]: any;
};
person = {
name: "John",
age: 25,
gender: "male",
address: "New York",
};
声明函数类型
typescript
let add: (x: number, y: number) => number; // 声明函数的参数类型和返回类型, TS语法, 非箭头函数
add = (x, y) => x + y; // add 只能赋值这种类型的函数, JS语法, 箭头函数和 function 均可
声明数组类型
typescript
let arr1: string[];
let arr2: Array<string>; // 使用泛型
arr1 = ["hello", "world"];
arr2 = ["hello", "world"];
tuple
tuple 是一种特殊的数组类型, 可以储存固定数量的元素, 各元素类型已知, 顺序一定。
typescript
let tuple1: [string, number];
let tuple2: [string, number, boolean?];
let tuple3: [string, number[]];
let tuple4: [string, ...number[]];
tuple1 = ["hello", 123];
tuple2 = ["hello", 123, true];
tuple3 = ["hello", [1, 2, 3]];
tuple4 = ["hello", 1, 2, 3];
enum
enum 类型是一种特殊的对象类型, 用于定义一组命名常量。
数字枚举
typescript
enum Direction {
Up = 0,
Down,
Left,
Right,
}
const walk = (direction: Direction) => {
if (direction === Direction.Up) {
console.log("Go up");
} else if (direction === Direction.Down) {
console.log("Go down");
} else if (direction === Direction.Left) {
console.log("Go left");
} else if (direction === Direction.Right) {
console.log("Go right");
}
};
数字枚举的成员会被赋值为从 0 开始递增的数字, 也可以手动指定成员的值。
本质上是一个对象, 属性是只读, 不能赋值
并且反向映射
typescript
// 控制台打印为:
{0:'Up', 1:'Down', 2:'Left', 3:'Right', Up: 0, Down: 1, Left: 2, Right: 3}
console.log(Direction[0]) // Up
console.log(Direction.Up) // 0
字符串枚举
typescript
enum Color {
Red = "red",
Green = "green",
Blue = "blue",
}
没有 反向映射
常量枚举
typescript
cosnt enum Direction {
Up = 0,
Down,
Left,
Right,
}
console.log(Direction.Up); // 0
编译后:
javascript
console.log(0 /* Direction.Up */);
可以减少代码量
type
基本类型别名
typescript
type shuzi = number;
lat a: shuzi;
a = 123;
联合类型
typescript
type status = number | string;
function slog(status: status): void {
console.log(status);
}
slog(404);
slog("404");
或者使用字面量
typescript
type gender = "male" | "female" | "other";
function sayHello(gender: gender): void {
console.log(`Hello,I am ${gender}!`);
}
sayHello("male");
sayHello("female");
sayHello("other");
交叉类型
typescript
type Person = {
name: string;
age: number;
gender: string;
};
type Student = {
school: string;
grade: number;
subject: string;
};
type PersonAndStudent = Person & Student;
let personAndStudent: PersonAndStudent = {
name: "John",
gender: "male",
school: "Harvard",
grade: 12,
subject: "Math",
age: 25,
};
特殊情况
typescript
type myfun = () => void;
let myfunvar: myfun = () => {
return "hello"; // 合法
};
console.log(myfunvar()); // hello
比如, 在某些操作:
typescript
const arr: number[] = [1, 2, 3, 4, 5];
const brr: number[] = [];
arr.forEach((item) => brr.push(item)); // 这时会返回lenth, 不是void
说白了, 就是为了兼容 js
类
类修饰符
- public: 默认修饰符, 允许在任何地方访问
- protected: 只允许在类的内部和子类中访问
- private: 只允许在类的内部访问
- readonly: 只读属性, 只能在声明时或构造函数中初始化, 可以和其他修饰符连用
属性的简写形式
简写前:
typescript
class Person {
public name: string;
age: number; // 不写修饰符默认public
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
}
简写后:
typescript
class Person {
constructor(public name: string, public age: number) {}
}
抽象类
不能被实例化, 只能被继承, 用于定义一组抽象方法和属性, 让子类去实现, 也可以有普通的方法
何时使用抽象类:
- 为一组相关的类定义通用的行为(方法和属性)
- 提供某些方法和实现, 让子类去继承
- 强制子类去实现抽象方法
- 共享代码逻辑
typescript
abstract class Person {
constructor(public name: string, public age: number, public gender: string) {}
abstract greet(): string;
introduce(): void {
console.log("this is PersonClass");
}
}
class Student extends Person {
constructor(name: string, age: number, gender: string, public major: string) {
super(name, age, gender);
}
greet(): string {
return `Hello, my name is ${this.name}, I am a ${this.age}-year-old ${this.gender} student majoring in ${this.major}.`;
}
}
const student1 = new Student("John", 20, "male", "Computer Science");
console.log(student1.greet());
接口
定义类结构
typescript
interface PersonInterface {
name: string;
age: number;
greet(): string;
}
class Person implements PersonInterface {
constructor(public name: string, public age: number) {}
greet(): string {
return `Hello, my name is ${this.name}, I am a ${this.age}-year-old person.`;
}
}
定义对象的接口
typescript
interface PersonInterface {
name: string;
age: number;
gender?: string;
greet(): string;
}
const person: PersonInterface = {
name: "John",
age: 25,
greet(): string {
return `Hello, my name is ${this.name}, I am a ${this.age}-year-old person.`;
},
};
定义函数的接口
typescript
interface AddInterface {
(x: number, y: number): number;
}
const add: AddInterface = (x, y) => x + y;
接口继承
typescript
interface AnimalInterface {
name: string;
eat(): void;
}
interface DogInterface extends AnimalInterface {
bark(): void;
}
class Dog implements DogInterface {
constructor(public name: string) {}
eat(): void {
console.log(`${this.name} is eating.`);
}
bark(): void {
console.log(`${this.name} is barking.`);
}
}
注: 接口可以自动合并, 并且一个类实现多个接口
泛型
泛型允许我们在定义函数, 接口, 类的时候, 使用类型参数来表示未指定的类型, 这些参数在具体使用时, 才被指定具体的类型, 泛型能让同一段代码适用于多种类型, 同时任然保持类型的安全性
泛型函数
typescript
function logData<T>(data: T) {
console.log(data);
}
logData<string>("hello");
logData<number>(123);
typescript
function logData<T, U>(data1: T, data2: U): T | U {
return Date.now() % 2 === 0 ? data1 : data2;
}
console.log(logData<string, number>("hello", 123));
console.log(logData<number, string>(123, "world"));
泛型接口
typescript
interface DataInterface<T> {
data: T;
}
const data1: DataInterface<string> = {
data: "hello",
};
泛型类
typescript
class DataClass<T> {
constructor(public data: T) {}
logData() {
return this.data;
}
}
const data1 = new DataClass<string>("hello");
类型声明文件
.d.ts, 在 ts 文件中引入 js 时, 需要使用声明文件对 js 进行类型声明, 否则会报错
装饰器
装饰器的本质是一种特殊的函数, 它可以对: 类、方法、属性、参数等进行扩展, 同时能让代码更加简洁
类装饰器
类装饰器是一个应用在类声明上的函数, 可以为类添加额外的功能, 或添加额外的逻辑
typescript
function Demo(target: Function) {}
@Demo
class Person {
constructor(public name: string, public age: number) {}
}
返回值
如果返回一个类, 则会替换原来的类
其他则不做影响
类型限制
Function 类型太宽泛了
可以用
typescript
type Constructor = new (...args: any[]) => {};
替换被装饰的类
typescript
type Constructor = new (...args: any[]) => {};
interface Person {
getCreationTime(): Date;
}
function logTime<T extends Constructor>(Target: T) {
return class extends Target {
createdTime: Date;
constructor(...args: any[]) {
super(...args);
this.createdTime = new Date();
}
getCreationTime() {
return this.createdTime;
}
};
}
@logTime
class Person {
constructor(public name: string, public age: number) {}
}
const person = new Person("John", 25);
console.log(person.getCreationTime());
装饰器工厂
一个能返回装饰器函数的函数
typescript
function logInfo(cut: number) {
return function (target: Function) {
target.prototype.introduce = function () {
for (let i = 0; i < cut; i++) {
console.log(this.name + " " + this.age);
}
};
};
}
interface Person {
introduce(): void;
}
@logInfo(2)
class Person {
constructor(public name: string, public age: number) {}
}
const p = new Person("John", 30);
p.introduce();
装饰器组合
ts 优先执行装饰器工厂, 从上到下, 所以先输出 test2 工厂, test3 工厂
再从下到上执行装饰器, test4, test3, test2, test1
typescript
@test1
@test2()
@test3()
@test4
class Person {
constructor(public name: string, public age: number) {}
}
属性装饰器
typescript
function Demo(target: object, key: string) {
console.log(target, key);
}
class Person {
@Demo name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
}
target: 构造函数(类)的 prototype
key: 属性名
若为静态属性, target 为类本身
方法装饰器
typescript
function logInfo(target: object, key: string, descriptor: PropertyDescriptor) {
console.log(target, key, descriptor);
}
class Person {
constructor(public name: string, public age: number) {}
@logInfo
speak() {
return "hello";
}
}
target: 构造函数(类)的 prototype
key: 属性名
descriptor: 描述符, 包含了该属性的各种信息, 如: value, get, set, enumerable, configurable, writable
若为静态属性, target 为类本身