文章目录
初识TS
npm install -g typescript 下载ts到全局
tsc -v 查看版本
同级创建tsconfig.json 文件 用于ts的配置
cmd中执行tsc hello.ts 可以创建出 js
ts代码报错也可以生成js文件
const hello = (name: string) => {
return `hello${name}`;
};
hello("liusheng");
// hello(123); 会报错,编译也会报错
// tsc -w class.ts 监听变化,生成js文件
// npm install -g ts-node 一步运行
// ts-node class.ts
tsc --init 生成配置文件
在线的TS演练场-尝试自己写一下
数据类型
基础类型
- boolean
- number
- string
- null和undefined
/*
原始数据类型
*/
// let isDone: boolean = 123;
let isDone: boolean = false;
let age: number = 13;
let binaryNumber: number = 0b1111; //es6 二进制和八进制
let firstName: string = "haohao";
let message: string = `Hello,${firstName},age is ${age}`;
let u: undefined = undefined;
let n: null = null;
// let num: number = "string"; //报错
let num: number = undefined;
void类型
- void类型像是与any类型相反,它表示没有任何类型。 当一个函数没有返回值时,你通常会见到其返回值类型是 void:
function warnUser(): void {
console.log("This is my warning message");
}
warnUser();
// 注意:声明一个void类型的变量没有什么大用,因为你只能为它赋予undefined和null
let unusable: void = undefined;
never类型
- 这个类型和 void 有些相似,也是不返回值,标示函数,比如:
function error(): never { throw new Error("error"); } error(); // function endless(): never { // while (true) { // console.log(111); // } // } // endless();
- 通俗理解 void 与 never:
- void 可以理解为一个没有 return 但能执行完毕的函数;
never 是一个函数体无法结束执行的函数;
any类型
/*
第三方,无法确定类型,any:宽泛的约束条件
*/
let notSure: any = "4";
notSure = `maybe it is a string`;
notSure = true;
notSure.myName;
notSure.getName();
// any类型 在.调用方法时没有提示
firstName.charCodeAt; //string 有提示
notSure.charCodeAt; //any 没提示
联合类型
/*
联合类型 可以赋值几个类型
*/
let numberOrString: number | string = 5;
numberOrString = "55";
// numberOrString = true; //报错
数组
/*
数组
类型 + [] 表示
*/
// let arrOfNumber: number[] = [1, 2, "str", 4];//报错
let arrOfNumber: number[] = [1, 2, 3, 4];
arrOfNumber.push(5);
// arrOfNumber.push("str");//报错
类数组
/*
类数组
*/
function test() {
console.log(arguments); //IArguments
// arguments.length
// arguments[1]
// arguments.forEach//报错,类数组找不到数组原型的方法
// let arr: any[] = arguments;//报错
}
元组
/*
元组 tuple
*/
// 第一项 字符串 第二项 boolean 第三项 数字 用any就丧失了意义
// let user: [string, number] = ["string"];//数组里多参数,少参数也会报错
let user: [string, number] = ["string", 1];
// user = [1, "string"];//报错,类型不能给错,按顺序
枚举
// enums 枚举
// 数字枚举
// enum Direction {
// Up,
// Down,
// Left,
// Right,
// }
// console.log(Direction.Up); //0
// console.log(Direction[0]); //'Up'
/*
// tsc 编译后 -> js文件
var Direction;
(function (Direction) {
Direction[Direction["Up"] = 0] = "Up";
Direction[Direction["Down"] = 1] = "Down";
Direction[Direction["Left"] = 2] = "Left";
Direction[Direction["Right"] = 3] = "Right";
})(Direction || (Direction = {}));
console.log(Direction.Up); //0
console.log(Direction[0]); //'Up'
*/
// 字符串枚举
// enum Direction {
// Up = "UP",
// Down = "DOWN",
// Left = "LEFT",
// Right = "RIGHT",
// }
// const value = "UP";
// if (value === Direction.Up) {
// console.log("go up!");
// }
// 常量枚举 const enum
const enum Direction {
Up = "UP",
Down = "DOWN",
Left = "LEFT",
Right = "RIGHT",
}
const value = "UP";
if (value === Direction.Up) {
console.log("go up!");
}
// tsc 编译后 -> js文件
// var value = "UP";
// if (value === "UP" /* Up */) {
// console.log("go up!");
// }
symbol
// sym2 can only be a constant reference. 不能用let
const sym2: unique symbol = Symbol();
bigint
- 从 ES2020 开始,JavaScript 中有一个用于非常大的整数的原语BigInt, 当目标低于ES2020时,BigInt字面量不可用。
// Creating a bigint via the BigInt function
const oneHundred: bigint = BigInt(100);
// Creating a BigInt via the literal syntax
const anotherHundred: bigint = 100n;
interface接口
/*
interface 接口 :规范 和 契约 规定了样子
对对象的形状进行描述
对类进行抽象
Duck Typing (鸭子类型)
描述 object 的样子
描述 类, 函数 ,数组 都可以用 interface 描述
*/
// 定义一个接口
// 完全匹配形状
interface Person {
//Person -> IPerson 有的名字前加I 告诉是 Interface
//这里面属性分隔就是分号分隔,不是逗号
name: string;
age: number;
}
// viking -> Person 类型 ,约束 viking 的 object 类型就是Person那样
let viking: Person = {
//对象里不能少个属性或者多个属性
name: "songJing",
age: 20,
};
// 不完全匹配形状 age?: number;
interface PersonLiu {
name: string;
age?: number;
}
let vikingLiu: PersonLiu = {
// age 可以写或者不写
name: "songJing",
};
// 创建的时候的只读属性 readonly
interface PersonShe {
readonly id: number; //用于属性上的 放在前面
name: string;
age?: number;
}
let vikingShe: PersonShe = {
id: 1245,
name: "songJing",
};
// vikingShe.id = 1111;//报错 只读属性,不能重新赋值
函数
// js中函数是一等公民
// 和其他类型平等 传参,存数组,被返回,赋值给变量 等
// function add(x: number, y: number, z: number = 10): number {
// // y: number, z?: number 必选参数不能位于可选参数后
// //(x: number, y: number): number 最后的 number 就是返回的类型
// // z: number = 10 es6 加默认值&&最后一个参数是可选的
// if (typeof z === "number") {
// return x + y + z;
// } else {
// return x + y;
// }
// }
// let result = add(2, 3);
// 函数表达式
const add = function add(x: number, y: number, z: number = 10): number {
// y: number, z?: number 必选参数不能位于可选参数后
//(x: number, y: number): number 最后的 number 就是返回的类型
// z: number = 10 es6 加默认值 最后一个参数是可选的
if (typeof z === "number") {
return x + y + z;
} else {
return x + y;
}
};
// :(x: number, y: number, z?: number) => number 冒号后面是ts中申明类型
// const add2: number = add; //报错
const add2: (x: number, y: number, z?: number) => number = add; //ts类型推测
// let str = "str";
// str = "ddd";
// str = 123;//报错,没有指定类型,ts会自己推测 -> 类型推论
类
类 :定义一切事物的抽象特点
对象:类的实例
面向对象 oop 封装,继承,多态
类中的修饰符
pulic 共有的
private 属性“name”为私有属性,只能在类“Animal”中访问,外部和子类也不可以访问
protected 子类可以访问属性,外部不可以访问
readonly 只读不能重新赋值
class Animal {
public name: string;
// private name: string;
// protected name: string;
// readonly name: string;
// 静态属性
static categoies: string[] = ["mammal", "bird"];
// 静态方法:当类中定义和实例的方法没有多大关系时
static idAnimal(a) {
return a instanceof Animal;
}
constructor(name: string) {
this.name = name;
}
run() {
return `${this.name} is running`;
}
}
console.log(Animal.categoies);
const snake = new Animal("lily");
console.log(Animal.idAnimal(snake));
// console.log(snake.run());
// console.log(snake.name);
snake.name = "lucy";
// console.log(snake.name);
class Dog extends Animal {
bark() {
return `${this.name} is bark`;
}
}
const maomao = new Dog("maomao");
// console.log(maomao.run());
// console.log(maomao.bark());
class Cat extends Animal {
constructor(name) {
super(name);
// console.log(this.name);
}
run() {
//super.run 调用父类的方法
return "Meow," + super.run();
}
}
const sanhua = new Cat("sanhua");
// console.log(sanhua.run());
类和接口
//两个类都用一个方法,用 interface 定义约束
// class Car {
// switchRadio() {}
// }
// class Cellphone {
// switchRadio() {}
// }
// interface 定义约束 implements 抽象类的属性和方法
// 抽象验证类的属性和方法
interface Radio {
// void 不返回
switchRadio(triggerL: boolean): void;
}
interface Battery {
checkBatteryStatus();
}
// 接口也可以继承
interface RadioWithBattery extends Radio {
checkBatteryStatus();
}
class Car implements RadioWithBattery {
switchRadio() {}
checkBatteryStatus() {}
}
// implements Radio, Battery 两个接口,用逗号隔开就行
class Cellphone implements Radio, Battery {
switchRadio() {}
checkBatteryStatus() {}
}
泛型
// BUG
// function echo(arg: any): any {
// return arg;
// }
// const result: string = echo(123);
// 在函数中使用泛型
//泛型出现的动机 ,在函数名称后面加 <> 里面可以写泛型的名称 T 类似占位符或者变量
function echo<T>(arg: T): T {
return arg;
}
// const str: string = "str";
// const result = echo(str);
// const result: string = echo(true);//报错,不会像之前一样不报错
function swap<T, U>(tuple: [T, U]): [U, T] {
return [tuple[1], tuple[0]];
}
const result2 = swap(["str", 12]);
// result2[1]. //调用字符串的方法
// 约束泛型
function echoWithArr<T>(arg: T[]): T[] {
console.log(arg.length);
return arg;
}
const arrs = echoWithArr([1, 2, 3]);
interface IWithLength {
length: number;
}
function echoWithLength<T extends IWithLength>(arg: T): T {
console.log(arg.length);
return arg;
}
const str = echoWithLength("str");
const obj = echoWithLength({ length: 10, width: 10 });
const arr2 = echoWithLength([1, 2, 3]);
// echoWithLength(13); //报错,类型“13”的参数不能赋给类型“IWithLength”的参数。
// 在类中使用泛型
class Queue<T> {
private data = [];
push(item: T) {
return this.data.push(item);
}
pop(): T {
return this.data.shift();
}
}
const queue = new Queue<number>();
queue.push(1);
console.log(queue.pop().toFixed());
const queue2 = new Queue<string>();
queue2.push("str");
console.log(queue2.pop().length);
// 在 interface 中使用泛型
interface KeyPair<T, U> {
key: T;
value: U;
}
let kp1: KeyPair<number, string> = { key: 123, value: "str" };
let kp2: KeyPair<string, number> = { key: "22", value: 123 };
let arr: number[] = [1, 2, 3];
let arrTwo: Array<number> = [1, 2, 3];
//
interface IPlus<T> {
(a: T, b: T): T;
}
function plus(a: number, b: number): number {
return a + b;
}
function connect(a: string, b: string): string {
return a + b;
}
const a: IPlus<number> = plus;
const b: IPlus<string> = connect;
命名空间
命名空间:
在代码量较大的情况下,为了避免各种变量命名相冲突,可将相似功能的函数、类、接口等放置到命名空间内
同Java的包、.Net的命名空间一样,TypeScript的命名空间可以将代码包裹起来,只对外暴露需要在外部访问的对象。
命名空间和模块的区别:
命名空间:内部模块,主要用于组织代码,避免命名冲突。(不同命名空间,类名称一样也可以)
模块:ts的外部模块的简称,侧重代码的复用,一个模块里可能会有多个命名空间。
/*
命名空间:在代码量较大的情况下,为了避免各种变量命名相冲突,可将相似功能的函数、类、接口等放置到命名空间内
*/
import { A, B } from "./animal";
var d = new A.Dog("小黑");
d.eat();
var dog = new B.Dog("毛毛");
dog.eat();
- animal.ts
export namespace A { interface Animal { name: string; eat(): void; } export class Dog implements Animal { name: string; constructor(theName: string) { this.name = theName; } eat() { console.log(`${this.name} 在吃狗粮。`); } } export class Cat implements Animal { name: string; constructor(theName: string) { this.name = theName; } eat() { console.log(`${this.name} 在吃猫粮。`); } } } export namespace B { interface Animal { name: string; eat(): void; } export class Dog implements Animal { name: string; constructor(theName: string) { this.name = theName; } eat() { console.log(`${this.name} 在吃狗粮。`); } } export class Cat implements Animal { name: string; constructor(theName: string) { this.name = theName; } eat() { console.log(`${this.name} 在吃猫粮。`); } } }
装饰器
通俗的讲装饰器就是一个方法,可以注入到类、方法、属性参数上扩展类、属性、方法、参数的功能。
常见的装饰器有:类装饰器、属性装饰器、方法装饰器、参数装饰器
装饰器的写法:普通装饰器(无法传参)、装饰器工厂(可传参)
装饰器是过去几年js最大的成就之一,以是Es7的标准特性之一
- 类装饰器:类装饰器在类声明之前被声明(紧靠着类声明)。类装饰器应用于类构造函数,可以用来监视,修改或替换类定义。
- 装饰器-ES6
- 装饰器-TypeScript
装饰器的实验支持
- tsconfig.json
{ "compilerOptions": { "experimentalDecorators": true //装饰者的实验支持 } }
装饰器的写法
- 普通装饰器(无法传参)
function logClass(params: any) {
console.log(params);
// params 就是当前类
params.prototype.apiUrl = "动态扩展的属性";
params.prototype.run = function () {
console.log("我是一个run方法");
};
}
@logClass
class HttpClient {
constructor() {}
getData() {}
}
var http: any = new HttpClient();
console.log(http.apiUrl);
http.run();
- 装饰器工厂(可传参)
function logClass(params: string) {
return function (target: any) {
console.log(target);
console.log(params);
target.prototype.apiUrl = params;
};
}
// @logClass("hello")
@logClass("zhainanya")
class HttpClient {
constructor() {}
getData() {}
}
var http: any = new HttpClient();
console.log(http.apiUrl);
类装饰器
- 重载构造函数
- 类装饰器表达式会在运行时当作函数被调用,类的构造函数作为其唯一的参数。
- 如果类装饰器返回一个值,它会使用提供的构造函数来替换类的声明
function logClass(target: any) {
console.log(target);
return class extends target {
apiUrl: any = "我是修改后的数据";
getData() {
this.apiUrl += "----";
console.log(this.apiUrl);
}
};
}
@logClass
class HttpClient {
public apiUrl: string | undefined;
constructor() {
this.apiUrl = "我是构造函数里面的apiUrl";
}
getData() {
console.log(this.apiUrl);
}
}
var http = new HttpClient();
http.getData();
属性装饰器
- 属性装饰器表达式会在运行时当作函数被调用,传入下列两个参数:
- 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
- 成员的名字。
// 类装饰器
function logClass(params: string) {
return function (target: any) {
// console.log(target);
// console.log(params);
};
}
// 属性装饰器
function logProperty(params: any) {
return function (target: any, attr: any) {
console.log(target);
console.log(attr);
target[attr] = params;
};
}
@logClass("xxx")
class HttpClient {
@logProperty("xxx.com")
public url: any | undefined;
constructor() {}
getData() {
console.log(this.url);
}
}
var http: any = new HttpClient();
http.getData();
方法装饰器
它会被应用到方法的属性描述符上,可以用来监视,修改或者替换方法定义。
方法装饰会在运行时传入下列三个参数:
- 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象
- 成员的名字
- 成员的属性描述符
- tsc class-decorator.ts --target ES5 -w --experimentalDecorators 运行
- 方法装饰器
function get(params: any) {
return function (target: any, methodName: any, desc: any) {
console.log(target);
console.log(methodName);
console.log(desc);
target.apiUrl = 123;
target.run = function () {
console.log("run");
};
};
}
class HttpClient {
public url: any | undefined;
constructor() {}
@get("xxx")
getData() {
console.log(this.url);
}
}
var http: any = new HttpClient();
console.log(http.apiUrl);
http.run();
- 方法装饰器
function get(params: any) {
return function (target: any, methodName: any, desc: any) {
console.log(target);
console.log(methodName);
console.log(desc, desc.value);
// 修改装饰器的方法,把装饰器方法里面传入的所有参数改为string类型
var oMthod = desc.value;
desc.value = function (...args: any[]) {
args = args.map((value) => {
return String(value);
});
console.log(args, oMthod);
oMthod.apply(this, args);
};
};
}
class HttpClient {
public url: any | undefined;
constructor() {}
@get("maomao")
getData(...args: any[]) {
console.log(args, "==");
console.log("getData=====");
}
}
var http: any = new HttpClient();
http.getData(123, "xxx");
方法参数装饰器
参数装饰器表达式会在运行时当作函数被调用,可以使用参数装饰器为类的原型增加一些元素数据,传入下列三个参数
- 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
- 方法的名称
- 参数在函数参数列表中的索引
function logParams(params: any) { return function (target: any, methodName: any, paramsIndex: any) { console.log(params); console.log(target); console.log(methodName); console.log(paramsIndex); target.apiUrl = params; }; } class HttpClient { public url: any | undefined; constructor() {} getData(@logParams("xxx") uuid: any) { console.log(uuid); } } var http: any = new HttpClient(); http.getData(123); console.log(http.apiUrl);
装饰器执行顺序
- 属性装饰器 -> 方法装饰器 -> 方法参数装饰器 -> 类装饰器
- 同一个方法有多个装饰器,会像剥洋葱一样,先从外到内进入,然后由内向外执行。
function logClass1(params: string) { return function (target: any) { console.log("类装饰器1"); }; } function logClass2(params: string) { return function (target: any) { console.log("类装饰器2"); }; } function logAttribute(params?: string) { return function (target: any, attrName: any) { console.log("属性装饰器"); }; } function logMethod(params?: string) { return function (target: any, attrName: any, desc: any) { console.log("方法装饰器"); }; } function logParams1(params?: string) { return function (target: any, attrName: any, desc: any) { console.log("方法参数装饰器1"); }; } function logParams2(params?: string) { return function (target: any, attrName: any, desc: any) { console.log("方法参数装饰器2"); }; } @logClass1("zhainanya-1") @logClass2("zhainanya-2") class HttpClient { @logAttribute() public apiUrl: string | undefined; constructor() {} @logMethod() getData() { return true; } setData(@logParams1() attr1: any, @logParams2() attr2: any) {} } var http: any = new HttpClient();
其他知识点
- 类型别名,联合类型,类型断言,声明文件 || 第三方模块
类型别名
- 类型别名:通常情况下,我们会多次使用同一个类型,并用一个名称来引用它。
- 使用类型别名来命名任何类型,而不仅仅是对象类型。例如,类型别名可以命名联合类型。
// type aliases 类型别名
type PlusType = (x: number, y: number) => number;
function sum(x: number, y: number) {
return x + y;
}
const sum2: PlusType = sum;
————
type FetchKeys = (data:any[]) => Promise<string[]>;
interface FetchKeys {
(data:any[]): Promise<string[]>
}
联合类型
// 联合类型
type NameResolver = () => string;
type NameOrResolver = string | NameResolver;
function getName(n: NameOrResolver): string {
if (typeof n === "string") {
return n;
} else {
return n();
}
}
// type assertion 不确定类型,就访问其中的类型或方法
类型断言
- 与类型注释一样,类型断言会被编译器删除,并且不会影响代码的运行时行为
- 由于类型断言在编译时被移除,因此不存在与类型断言相关的运行时检查。即使类型断言错误,也不会引发任何异常或生成任何异常。
//类型断言 -》 不是类型转换 断言成联合类型里没有的会报错
function getLength(input: string | number): number {
// const str = input as String; //interface
// if (str.length) {
// return str.length;
// } else {
// const number = input as Number;
// return number.toString().length;
// }
if ((<string>input).length) {
return (<string>input).length;
} else {
return input.toString().length;
}
}
非空断言运算符
- 非空断言运算符(后缀!)
function liveDangerously(x?: number | null) { // No error console.log(x!.toFixed()); //'x' is possibly 'null' or 'undefined'. //console.log(x.toFixed()); }
类型守卫
- 当我们在处理复杂的类型时,有时为了方便地对类型进行判断和处理,我们需要用到类型守卫。类型守卫是一种类型保护机制,它可以将变量的类型缩小并且避免由此引起的错误
// typeof
function double(x: number | string): number | string {
if (typeof x === 'number') {
return x * 2;
} else if (typeof x === 'string') {
return x + x;
}
}
//instanceof
class MyClass {}
class MySubClass extends MyClass {}
function logClass(x: MyClass | MySubClass) {
if (x instanceof MySubClass) {
console.log('x is an instance of MySubClass');
} else if (x instanceof MyClass) {
console.log('x is an instance of MyClass');
}
}
//in
type Person = { name: string; age?: number };
function greet(person: Person) {
if ('age' in person) {
console.log(`Hello ${person.name}, you are ${person.age} years old!`);
} else {
console.log(`Hello ${person.name}, nice to meet you!`);
}
}
//is
type Point = { x: number; y: number };
function isPoint(p: Point | any): p is Point {
return typeof p.x === 'number' && typeof p.y === 'number';
}
function distance(p1: Point | any, p2: Point | any) {
if (isPoint(p1) && isPoint(p2)) {
return Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2));
} else {
throw new Error('Invalid arguments: expected points');
}
}
索引类型
三种索引类型包括:keyof 类型操作符、索引访问类型和类型映射类型。
- keyof 类型操作符:可以用来获取对象或类的所有属性名,它返回一个类型为字符串字面量联合类型的结果
interface Person {
name: string;
age: number;
}
type PersonKeys = keyof Person; // PersonKeys 的结果为 "name" | "age"
- 索引访问类型:可以用来获取对象或数组的某个属性或元素的类型。它的语法是 Type[KeyType],其中 Type 表示要访问的对象或数组的类型,KeyType 表示属性或元素的键的类型
interface Person { name: string; age: number; }
- 在 TypeScript 中,使用索引访问类型 [T] 时,如果 T 是一个联合类型,它会对联合类型中的每个成员进行索引访问,并将结果合并成一个新的联合类型。
type AllFields = 'name' | 'age' | 'email'; // 定义 ExclusiveSort 类型 type ExclusiveSort<T extends string, K extends T> = { [P in T]: P extends K ? 'asc' | 'desc' : false; }; // 定义 Sortable 类型 type Sortable<T extends string> = { [K in T]: ExclusiveSort<T, K>; }[T]; type SortableResult = Sortable<AllFields>; // type SortableResult = ExclusiveSort<AllFields, "name"> | ExclusiveSort<AllFields, "age"> | ExclusiveSort<AllFields, "email">
- 在 TypeScript 中,使用索引访问类型 [T] 时,如果 T 是一个联合类型,它会对联合类型中的每个成员进行索引访问,并将结果合并成一个新的联合类型。
- 类型映射类型:允许我们将一个类型映射到另一个类型。它的语法是 { [P in K]: V },其中 P 表示新类型的属性名,K 表示原类型的属性名(可以是 keyof 类型操作符得到的结果),V 表示新类型的属性值
interface Person {
name: string;
age: number;
}
type ReadonlyPerson = { readonly [P in keyof Person]: Person[P] };
模版字面量类型
允许我们在类型定义中使用字符串模板。这样做的好处是,可以更灵活地定义类型信息,特别是在定义联合类型时
type EventName = 'click' | 'mouseover' | 'keydown';
type EventFullName<T extends string> = `${T}Event`;
type ClickEventFullName = EventFullName<EventName>; //EventFullName<'click'> ClickEventFullName 的结果为 "clickEvent"
let clickName:ClickEventFullName = 'clickEvent'
映射修饰符
映射修饰符可以用来批量地修改类型映射类型中的属性。映射修饰符包括三种,分别是 readonly、? 和 +/-。
- readonly:指定所有属性为只读属性,语法为 { readonly [P in K]: V }。
- ?:指定所有属性为可选属性,语法为 { [P in K]?: V }。
- +/-:指定属性的修饰符,可以是 +,表示添加修饰符,或者是 -,表示移除修饰符。例如,{ [+P in K]: V } 表示添加 readonly 修饰符。
interface Person {
readonly name: string;
readonly age: number;
}
type ReadonlyPerson = { readonly [P in keyof Person]: Person[P] };
type OptionalPerson = { [P in keyof Person]?: Person[P] };
——————————————————————————————————————————————————
interface Person {
readonly name: string;
readonly age: number;
}
type OptionalPerson = { -readonly [P in keyof Person]?: Person[P] };
声明文件
// 声明文件 declare 同级文件 jQuery.d.ts
declare var jQuery: (selector: string) => any;
第三方模块
// 第三方模块
// npm install --save @types/jquery
*/
实用工具类型
- 实用工具类型
- TypeScript 提供了几种实用程序类型来促进常见的类型转换
Awaited 是 TypeScript 4.5 版本引入的一个内置工具类型,其主要用途是提取 Promise 类型的解析值类型,或者递归地解开嵌套的 Promise 类型
Partial 将类型 T 的所有属性变为可选
Required 将类型 T 的所有属性变为必需
Readonly 将类型 T 的所有属性设置为只读,这样就无法修改这些属性
Record 创建一个类型,其属性键为 K,属性值为 T
Pick 从类型 T 中选取一组属性 K(K 必须是 T 的属性)来构造类型
Exclude 从类型 T 中排除那些可以赋值给 U 的类型
Omit 从类型 T 中排除一组属性 K 后的类型
Extract 从类型 T 中提取那些可以赋值给 U 的类型
NonNullable 从类型 T 中排除 null 和 undefined
ReturnType 获取函数类型 T 的返回类型
InstanceType 获取构造函数类型 T 的实例类型
Parameters 获取函数类型 T 的参数类型作为一个元组
ConstructorParameters 提取构造函数类型的参数类型,并将这些参数类型组合成一个元组类型
NoInfer [TypeScript 5.4版本 ]在泛型参数传递时,该类型参数不会被自动推断,而是由开发者手动指定
- Awaited<Type> 处理异步函数返回的 Promise 时, 处理不同的 Promise 嵌套情况 - type Awaited<T> = T extends null | undefined ? T : T extends object & { then(onfulfilled: infer F, ...args: infer _): any; } ? F extends (value: infer V, ...args: infer _) => any ? Awaited<...> : never : T - Partial 将类型 T 的所有属性变为可选 - type Partial<T> = { [P in keyof T]?: T[P] | undefined; } - Make all properties in T optional - Required 将类型 T 的所有属性变为必需 - type Required<T> = { [P in keyof T]-?: T[P]; } - Make all properties in T required - Readonly 将类型 T 的所有属性设置为只读,这样就无法修改这些属性 - type Readonly<T> = { readonly [P in keyof T]: T[P]; } - Make all properties in T readonly - Record 创建一个类型,其属性键为 K,属性值为 T - type Record<K extends string | number | symbol, T> = { [P in K]: T; } - Construct a type with a set of properties K of type T - Pick 从类型 T 中选取一组属性 K(K 必须是 T 的属性)来构造类型 - type Pick<T, K extends keyof T> = { [P in K]: T[P]; } - From T, pick a set of properties whose keys are in the union K - Exclude 从类型 T 中排除那些可以赋值给 U 的类型 值的组合 - type Exclude<T, U> = T extends U ? never : T - Exclude from T those types that are assignable to U - Omit 从类型 T 中排除一组属性 K 后的类型 - type Omit<T, K extends string | number | symbol> = { [P in Exclude<keyof T, K>]: T[P]; } - Construct a type with the properties of T except for those in type K. - Extract 从类型 T 中提取那些可以赋值给 U 的类型 - type Extract<T, U> = T extends U ? T : never - Extract from T those types that are assignable to U - NonNullable 从类型 T 中排除 null 和 undefined - type NonNullable<T> = T & {} - Exclude null and undefined from T - ReturnType 获取函数类型 T 的返回类型 - type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any - Obtain the return type of a function type - InstanceType 获取构造函数类型 T 的实例类型 - type InstanceType<T extends abstract new (...args: any) => any> = T extends abstract new (...args: any) => infer R ? R : any - Obtain the return type of a constructor function type - Parameters 获取函数类型 T 的参数类型作为一个元组 - type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never - Obtain the parameters of a function type in a tuple - ConstructorParameters 提取构造函数类型的参数类型,并将这些参数类型组合成一个元组类型 - type ConstructorParameters<T extends abstract new (...args: any) => any> = T extends abstract new (...args: infer P) => any ? P : never - Obtain the parameters of a constructor function type in a tuple - NoInfer 某个参数不被自动推断,而是手动指定以确保类型的精确性时 - type NoInfer<T> = intrinsic - Marker for non-inference type position
- TypeScript 提供了几种实用程序类型来促进常见的类型转换
字符串操作类型 Intrinsic String Manipulation Types
Uppercase<StringType>
Lowercase<StringType>
Capitalize<StringType>
Uncapitalize<StringType>
// Uppercase 示例 将字符串字面量类型转换为大写形式
// 单个字符串字面量类型
type LowercaseString = 'hello';
type UppercaseResult = Uppercase<LowercaseString>;//"HELLO"
// 字符串字面量联合类型
type MixedCaseStrings = 'hello' | 'world';
type AllUppercase = Uppercase<MixedCaseStrings>;
// Lowercase 示例 用于将字符串字面量类型转换为小写形式
type UppercaseString = 'HELLO';
type LowercaseResult = Lowercase<UppercaseString>;// 'hello'
// Capitalize 示例 用于将字符串字面量类型的首字母转换为大写。
type lowercaseString = 'hello';
type CapitalizedResult = Capitalize<lowercaseString>;
// Uncapitalize 示例 用于将字符串字面量类型的首字母转换为小写。
type CapitalizedString = 'Hello';
type UncapitalizedResult = Uncapitalize<CapitalizedString>;
自定义类型
列表每个字段单独可排序的,其他字段的类型为 false
// 定义 SortOrder 类型
type SortOrder = 'asc' | 'desc';
// 定义 ExclusiveSort 类型
type ExclusiveSort<T extends string, K extends T> = {
[P in T]: P extends K ? SortOrder : false;
};
// 定义 Sortable 类型
type Sortable<T extends string> = {
[K in T]: ExclusiveSort<T, K>;
}[T];
// 示例使用
type AllFields = 'name' | 'age' | 'email';
type SortableResult = Sortable<AllFields>;
// 展开 SortableResult 类型如下
// type SortableResult =
// | { name: SortOrder; age: false; email: false; }
// | { name: false; age: SortOrder; email: false; }
// | { name: false; age: false; email: SortOrder; }
let validSortA: SortableResult = { name: 'asc', age: false, email: false }; // OK
let validSortB: SortableResult = { name: false, age: 'desc', email: false }; // OK
常见问题
window 添加自定义属性
- global.d.ts文件
declare global {
interface Window {
RM_FONT_FACE: string;
RM_ANIMATION: string;
}
}
export {};
interface 和 type 区别
TypeScript 中的 interface 和 type 都可以用来定义对象的形状或者函数签名,但它们在某些方面有所不同。下面是 interface 和 type 的一些主要区别:
- 扩展性(Extensibility)
interface:可以被扩展以构建新的接口。通过使用 extends 关键字,接口可以扩展一个或多个现有的接口。
type:不可以被扩展,但可以通过交叉类型(使用 & 操作符)来组合现有的类型。 - 重复声明(Duplicate Declarations)
interface:可以在同一作用域内多次声明同一个接口,其成员会自动合并。
type:不允许同名的重复声明。 - 使用场景(Use Cases)
interface:推荐在定义对象的形状或实现类的场景中使用。它支持声明合并,这在声明文件(例如 .d.ts 文件)或声明类和函数库时十分有用。
type:适用于定义联合类型、交叉类型、元组类型等复杂类型。类型别名可以声明几乎任何类型,包括基本类型、联合类型和交叉类型。 - 实现(Implementation)
interface:可以通过类来实现(使用 implements 关键字)。
type:不能被类实现,因为它们可以代表非对象类型(如原始类型、联合和交叉类型)。 - 性能(Performance)
interface:在某些情况下,interface 可能会得到更好的性能,因为它们更容易被TypeScript编译器优化。
type:由于类型别名可能会直接展开,所以在某些复杂的类型组合中可能会导致编译器和IDE性能下降。
可选的使用
- 在 TypeScript 里,value: number | undefined; 和 value?: number 虽然在某些情况下表现相似,但实际上存在明显区别
- value: number | undefined;
- value 属性是必须存在的,但其类型可以是 number 或者 undefined
- value?: number
- value 属性是可选的,可以不提供 value 属性。如果提供了 value 属性,其类型必须是 number。