TypeScript核心类型系统完全指南
第一部分:TypeScript基础入门
TypeScript简介
1.什么是TypeScript
-
TS是JS的超集,简单来说就是为 js添加了类型限定。众所周知js的类型系统存在 先天的缺陷,程序中很多的问题都是因为错误的 类型导致的。ts属于静态类型编程语言,js属于动态编程语言
2. Ts的优势
- ts是前端项目的首选语言,ts中存在类型推断机制 不需要在代码中的每个地方都显示标注
体验TS与配置
体验ts
-
ts交于js会对数据类型进行检查
-
只需要第一次 定义变量的时候对 数据类型进行注解
let age:number=18; //:number 是类型注解 表示age变量的类型是number
常见的TS类型注解
-
原始类型
numberstringbooleansymbolnullundefined
-
对象类型
-
object(数组 对象 函数) -
联合类型
- 自定义类型(类型别名)
- 接口
- 元组
- 字面量类型
- 枚举
voidany
配置tsconfig.json
-
需要在根目录中被指ts的配置文件,这是ts开发必备的 操作之一
-
{"compilerOptions": {"target": "ES2020","module": "CommonJS","strict": true, "esModuleInterop": true,"skipLibCheck": true,"forceConsistentCasingInFileNames": true} }
-
第二部分:TypeScript核心类型系统
基础类型
原始类型
原始类型就是常见的 字符串 数字 布尔值 空 和 未定义 等等类型 基本上js怎么使用那么ts就这么使用 只是需要添加一些类型注解而已
let age: number=18;
let name: string='张三';
let sex: boolean=true;
let symbol: symbol=Symbol('123');
let nullValue: null=null;
let undefinedValue: undefined=undefined;
//js 里面怎么用 ts就是怎么用
数组类型
数组类型属于对象类型,在对象类型中每个子类型都有自己的细分语法
数组类型的类型注解: number[](推荐使用) 或者 :Array<string> 如果数组中存在多种数据类型就使用联合类型 (number | string)[]
//推荐写法
let numbers: number[]=[1,2,3,4,5];
let objArr:Object[]=[{},{}]
//其他写法
let strings:Array<string>=['1','2','3'];
//数组中含有多种类型数据 --> 联合类型
let arr: (number | string)[]=[1,'2',3,'4',5];
元组类型
元组可以看作确定元素个数与类型的数组,在部分场景里面会 使用到确定元素个数的数组类型 这种类型就叫做元组类型(比如:地图的经纬度)
元组类型类型注解::[number,number]
let Position=[31.232,12.653];
//元组的类型也不一定必须要一样
let Position2:[number,string]=[31.232,'12.653'];
枚举类型
枚举类型可以作为字面量类型的平替方案 枚举类型类似于字面量类型+联合类型的组合形态
枚举的值称为命名常量
定义枚举:
通过enum关键词定义 使用{}包裹命名常量
enum Direction{up,down,right,left}
枚举类型使用:
当使用枚举类型的函数需要调用的时候 只能通过枚举命名常量的属性来作为函数的参数
// 方向枚举
enum Direction {Up,Down,Left,Right
}// 状态枚举
enum Status {Pending = "PENDING",Approved = "APPROVED",Rejected = "REJECTED"
}// 函数参数使用枚举
function move(direction: Direction) {switch (direction) {case Direction.Up:console.log("向上移动");break;case Direction.Down:console.log("向下移动");break;// ...其他情况}
}// 调用函数时使用枚举
move(Direction.Up);
枚举的种类
1.数字枚举
数字枚举是最常见的枚举类型,默认情况下第一个成员的值为0,后续成员按顺序递增:
enum Direction {Up, // 值为 0Down, // 值为 1Left, // 值为 2Right // 值为 3
}// 使用枚举
let dir: Direction = Direction.Up; // 值为 0
console.log(dir); // 输出: 0
可以手动设置枚举成员的值:
// 设置起始值
enum Direction {Up = 1, // 值为 1Down, // 值为 2Left, // 值为 3Right // 值为 4
}// 为每个成员设置具体值
enum Direction {Up = 1,Down = 3,Left = 5,Right = 9
}
2.字符枚举:
字符串枚举的每个成员都必须显式地初始化为字符串字面量,它们没有自增长行为:
enum Direction { Up = "UP", Down = "DOWN", Left = "LEFT", Right = "RIGHT" }
// 使用字符串枚举
let dir: Direction = Direction.Up;
console.log(dir); // 输出: "UP"
- 异构枚举
虽然不建议使用,但技术上是可以混合字符串和数字成员的:
typescript
enum BooleanLikeHeterogeneousEnum { No = 0, Yes = "YES", }
字面量类型
字面量类型是一种特殊的类型,它将变量的类型限制为特定的值。与普通的string、number、boolean类型不同,字面量类型不仅指定了值的类型,还指定了值的具体内容。
字面量类型的特点:
- 精确性:字面量类型比普通类型更加精确,限定了变量只能是特定的值
- 常量推断:使用
const声明的变量,TypeScript会自动推断为字面量类型 - 可组合性:通过联合类型(
Union Types)可以组合多个字面量类型
字面量类型用法:
-
let str1='hello ts' //string类型 const str2='hello ts' //hello ts 字面量类型 -
字面量类型通常与联合类型结合使用,用于限制函数参数或配置对象的取值范围
// 限制函数参数的取值 function setPosition(direction: 'left' | 'right' | 'up' | 'down') {// 函数体 }// 调用时只能传入指定的字符串字面量 setPosition('left'); // 正确 setPosition('forward'); // 错误// 配置对象的属性限制 interface Config {theme: 'light' | 'dark';size: 'small' | 'medium' | 'large'; }const config: Config = {theme: 'dark', // 只能是'light'或'dark'size: 'medium' // 只能是'small'、'medium'或'large' };
与枚举类型相比
通常字面量类型与枚举类型可以替换
// 使用字面量类型
type Direction = 'up' | 'down' | 'left' | 'right';// 使用枚举类型
enum DirectionEnum {Up,Down,Left,Right
}
联合类型
联合类型(Union Types)是TypeScript中的一种高级类型特性,它允许一个变量或参数可以是多种类型中的一种。联合类型使用竖线(|)分隔每个类型,表示"或"的关系。 后续在高级类型特性里面会详细介绍联合类型的各种使用方法
基本语法:
// 基本语法:Type1 | Type2 | Type3
let value: number | string;
value = 123; // 正确,number类型
value = "hello"; // 正确,string类型
value = true; // 错误,boolean类型不在联合类型中
复杂类型
对象类型
对象类型就是在描述对象的结构与各个属性的类型与 方法类似
1.基本写法
let person:
{name:string;age:number;sayHi(name:string,age:number):void
} = {name:'张三',age:18,sayHi(name:string):void{console.log('hi',name)}
}
2.箭头函数写法 :
//箭头函数写法
let person2:{name:string;age:number;// 箭头的后面写返回值类型 sayHi:(name:string)=>void
} = {name:'张三',age:18,sayHi:(name:string):void=>{console.log('hi',name)}
}
3.对象类型的可选属性
//对象类型可选属性
let person3:{name:string;age:number;// 箭头的后面写返回值类型 sayHi:(name:string)=>void;sex?:string;
} = {name:'张三',age:18,sayHi:(name:string):void=>{console.log('hi',name)},//sex:'man' //可有可无
}
函数类型
函数类型就是在js的基础上单独为行数的 参数 与返回值类型进行类型标注.
单独标注参数类型与返回值类型
就是单对为函数的参数与返回值进行类型标注
function add(X:number,Y:number):number{return X+Y;
}
同时标注二者类型
这是第一种写法 可读性很差 前面两个类型定义是定义两个参数的 后面一个类型定义是定义返回值类型的
const add2:(num1:number,num2:number)=>number=(num1,num2)=>{return num1+num2;
}
下面是可读性更高的一中 写法
// 定义函数类型别名
type AddFunction = (num1:number, num2:number) => number;// 使用类型别名
const add2:AddFunction = (num1, num2) => {return num1 + num2;
}
箭头函数常用的定义方法
const add1 = (X:number, Y:number):number => {return X + Y;
}
返回值为void
function add3(X:number,Y:number):void{console.log(X+Y);
}
可选参数类型
在参数后加一个? 就是可选参数 但是不建议使用可选参数在需要计算的函数中. 值得注意的是 必选参数一定要放在可选参数的前面
function add4(X:number,Y?:number):void{console.log(X+(Y??1));
}//必选参数不能位于可选参数之后
function mySlice(start?:number,end?:number):void{console.log('开始',start,'结束',end);
}
mySlice()
特殊类型
any类型
any类型是我们极不推荐使用的类型 因为any类型不会对代码进行保护 和js基本没有两样了. 如果每个变量都说用any类型,那代码就和js基本一模一样了,失去了ts作为静态类型语言的作用了
let a:any = 123;
a = '123';
//any类型会忽略类型检查 还不如不用ts
void类型
void类型表示没有任何类型,通常用于函数没有返回值的情况。如果变量被注解为void类型一般只能赋值为null或者是undefined
基本用法
// 函数没有返回值时,返回类型标记为void
function sayHello(): void {console.log("Hello!");// 不需要return语句,或者可以return;
}// 等同于
function sayHello2(): void {console.log("Hello!");return; // 可以显式返回undefined
}// 变量声明为void类型(不常用)
let unusable: void = undefined; // void类型只能赋值为undefined或null
实际应用场景
// 事件处理函数通常没有返回值
function handleClick(event: Event): void {console.log("按钮被点击了");
}// 日志记录函数
function logMessage(message: string): void {console.log(`[LOG]: ${message}`);
}
null和undefined
在TypeScript中,null和undefined都有各自的类型,分别是null和undefined类型。
基本用法
// null类型
let nullValue: null = null;// undefined类型
let undefinedValue: undefined = undefined;// 在严格模式下,null和undefined只能赋值给any类型和它们各自类型
let num: number = null; // 错误:在严格模式下不允许
let str: string = undefined; // 错误:在严格模式下不允许
与联合类型结合使用
// 变量可以是字符串或null
let userName: string | null = null;
userName = "张三"; // 正确// 变量可以是数字或undefined
let userAge: number | undefined = undefined;
userAge = 25; // 正确// 函数返回值可能是对象或null
function findUser(id: number): User | null {// 查找用户逻辑// 如果找到返回User对象,否则返回nullreturn null;
}
在React中的应用
// React中常见的状态初始化为null
const [user, setUser] = useState<User | null>(null);// 使用可选链操作符安全访问属性
console.log(user?.name); // 如果user为null,不会报错
never类型
never类型表示永远不会发生的值的类型。它是TypeScript类型系统中的底部类型
使用场景
// 1. 函数抛出异常,永远不会有返回值
function throwError(message: string): never {throw new Error(message);
}// 2. 函数中有无限循环,永远不会结束
function infiniteLoop(): never {while (true) {// 无限循环}
}// 3. 类型守卫中的never
function exhaustiveCheck(value: never): never {throw new Error(`Unexpected value: ${value}`);
}
类型特点
- 底部类型:
never是TypeScript类型系统中的底部类型,它是所有类型的子类型 - 不可赋值:除了
never本身,没有其他值可以赋值给never类型 - 类型推断:在某些情况下,
TypeScript会自动推断出never类型
第三部分:类型高级特性
类型别名(Type Alias)
类型别名 即为自定义类型 当统一类型被多次使用时,可以通过类型别名 简化该类型的使用
定义与使用
使用type关键字来创建类型别名
// 使用type关节子创建类型别名
type myType=(number|string)[]
let arr1:myType=[1,2,'3'];
console.log(arr1);
接口(Interface)
一般情况下如果一个对象类型被多次使用的时候,为了达到复用的目的,会使用接口来描述对象的类型
定义:
interface Person{name:string;age:number;sayHi():void;
}let person1:Person={name:'张三',age:18,sayHi(){console.log('hi',this.name)}}
接口的继承
接口可以使用extends来继承另一个接口中的类型注解
//如果两个接口有公共属性 就可以通过继承的方式实现复用
interface People{name:string;
}interface Teacther extends People{age:number;subject:string;
}let t: Teacther={name:'张三',age:18,subject:'Math'
}
接口的合并
// 接口声明合并
interface Window {title: string;
}interface Window {ts: TypeScriptAPI;
}// 现在Window接口有title和ts两个属性
接口与类型别名:
接口和类型别名很相似,都可以为对象指定类型. 但是区别也是很明显的
- 接口可以通过继承来拓展自身的类型注解
- 接口可以通过合并拓展自身的类型注解
- 类型别名可以为任何类型创建别名,但是接口只适用于对象
//结构与类型别名都可以为对象指定类型
type APerosn={name:string,age:number
}interface BPerson{name:string,age:number
}let a:APerosn={name:'张三',age:18
}let b:BPerson={name:'张三',age:18
}
类(Class)
1.类的定义
TS中也引入了class的语法糖,写法基本和js中的class语法糖相似 不同的是 需要提前定义类中属性与方法的类型注解
//ts引入了class语法
class Person{name:string;age:number;//构造函数就是一种方法 所以参数必须规定类型constructor(name:string,age:number){this.name = name;this.age = age;}sayHi(){//因为没有return TS自动做了类型推断 所以这个方法可以不用写void返回类型console.log(`大家好,我叫${this.name},今年${this.age}岁`);}//类的实例方法和对象的方法是一样的 也需要指定类型changeName(name:string){this.name=name;}
}
// 如果类没有类型 会自动默认为any属性
let p1=new Person('张三',18)
2.类的继承
类的继承分为两种 一种是继承父类 另一种是继承接口中的类型定义
类的继承和其他语言面向对象类似,可以重写继承来的方法 也可以省略不写 使用父类继承来的方法. 而接口的继承和jva`是类似的.
//ts有两种继承方法 1.extends(继承父类) 2.implements(实现接口 ts特有)
class Animal{move(){console.log('move');}
}class Dog extends Animal{bark(){console.log('bark');}
}let dog=new Dog();
dog.bark()//2.implements 继承interface接口
//和java一样 接口只能定义属性和抽象方法(没有实现的方法)
interface Person{name:string;age:number;move():void
}class Student implements Person{name: string;age: number;constructor(name:string,age:number){this.name=name;this.age=age;}move(){console.log('move');}
}
3.类成员的可见性
类中的成员属性有四种访问修饰符 不仅仅是修饰属性的 也可以修饰方法
public- 公开的,任何人都可以访问(默认)private- 私有的,只能在类内部访问protected- 受保护的,只能在类和子类中访问readonly- 只读的,只能在声明时或构造函数中初始化
演示代码
// 基类 - 演示所有四种访问修饰符
class Person {// 1. public - 公开的,任何人都可以访问(默认)public name: string;// 2. private - 私有的,只能在类内部访问private secret: string;// 3. protected - 受保护的,只能在类和子类中访问protected age: number;// 4. readonly - 只读的,只能在声明时或构造函数中初始化readonly id: number;constructor(name: string, secret: string, age: number, id: number) {this.name = name;this.secret = secret;this.age = age;this.id = id;}// 公共方法可以访问所有成员public introduce(): void {console.log(`我叫${this.name},年龄${this.age},ID: ${this.id}`);// console.log(this.secret); // 可以在类内部访问private成员}// 私有方法只能在类内部调用private tellSecret(): void {console.log(`我的秘密是: ${this.secret}`);}
}// 子类 - 继承Person类
class Student extends Person {public grade: string;constructor(name: string, secret: string, age: number, id: number, grade: string) {super(name, secret, age, id);this.grade = grade;}public study(): void {console.log(`${this.name}正在学习`);// 可以访问protected成员console.log(`年龄: ${this.age}`);// 不能访问private成员 - 会报错// console.log(this.secret); // Error: Property 'secret' is private// 可以访问public成员console.log(`ID: ${this.id}`);}
}// 测试代码
const person = new Person('张三', '我喜欢吃糖', 25, 1001);
const student = new Student('李四', '我害怕考试', 18, 1002, '高三');// 1. public成员 - 可以任意访问
console.log(person.name); // 输出: 张三
console.log(student.name); // 输出: 李四// 2. private成员 - 不能在类外部访问console.log(person.secret); // Error: Property 'secret' is privateconsole.log(student.secret); // Error: Property 'secret' is private// 3. protected成员 - 不能在类外部访问console.log(person.age); // Error: Property 'age' is protectedconsole.log(student.age); // Error: Property 'age' is protected// 4. readonly成员 - 可以读取但不能修改
console.log(person.id); // 输出: 1001
// person.id = 1003; // Error: Cannot assign to 'id' because it is a read-only property// 调用方法
person.introduce(); // 输出: 我叫张三,年龄25,ID: 1001
student.introduce(); // 输出: 我叫李四,年龄18,ID: 1002
student.study(); // 输出: 李四正在学习\n年龄: 18\nID: 1002// 尝试修改readonly属性 - 编译时会报错student.id = 1005;
// Error: Cannot assign to 'id' because it is a read-only property
泛型(Generics)
泛型基础概念
钻石运算符 <> 里面添加的是类型变量比如T 这个T是一个变量 往里填哪个类型 T就是什么类型 他是一个类型的容器 可以自动捕获用户提供的类型.
// 泛型是可以在保证安全的清况等下 让函数与多种类型一起工作 从而实现复用 常用于函数 接口 类中
// <>叫做钻石运算符 里面添加类型变量 比如T等等
//T是一个特殊的变量 他的处理类型不是值 他是一个类型的容器 可以自动捕获用户提供的类型
function id<T>(value:T):T{return value;
}
const getId=<t>(Value:t):t=>{return Value;
}
//调用
const num=<string>getId('123')
const num1=<number>getId(123)//简化调用 调用泛型函数的时候 可以把尖括号省了 ts会自动识别类型(类型参数推断)
const num2=getId(123);
//有时候推断的类型可能不准确 就需要手动去定义
泛型约束
默认情况下 泛型函数的类型数量type可以代表多个类型 这导致无法访问任何属性 比如id(‘a’)调用函数时参数的长度
function id<T>(value:T):T{console.log(value.length);return value
}
使用上面的函数会报错 因为T可以代表任意类型 无法保证一定存在length属性 此时就需要为泛型添加约束来收缩类型
有两种为泛型添加约束的方法
-
方法1: 为type指定更具体的类型
function id<T>(value:T[]):T[]{console.log(value.length);return value } -
方法2: 定义接口为T添加约束
interface LengthWise{length:number } function id<T extends LengthWise>(value:T):T{console.log(value.length);return value }- 方法二的解释:
- 1.创建接口提供需要的属性 比如length
- 2.通过extends关键字使用该接口 为泛型(控制变量)添加约束
- 表述为 传入的类型必须具有length属性
- 方法二的解释:
多个泛型相互约束:
泛型的类型变量可以存在多个 而且类型变量之间也可以约束的 (比如 第二个类型变量受第一个变量的约束) 比如创建一个函数来获取兑现中属性的值
//泛型的类型变量可以存在多个 而且类型变量之间也可以约束的 (比如 第二个类型变量受第一个变量的约束) 比如创建一个函数来获取兑现中属性的值function getProp<T,K extends keyof T>(Obj:T,key:K):T[K]{return Obj[key];
}
let person={name:'jack',age:18}
console.log(getProp(person,'name')); //jack
//keyof关键字会接受一个对象类型 生成其键名称(可能是字符串或是数字)的联合类型
//实例中keyof T实际上获取的是person对象所有键的联合类型 也就是'name'|'age'
//类型变量k受T的约束 可以理解为 k只能是t所有键的任意一个
泛型接口与泛型类
泛型接口:
接口也可以配合泛型使用.
// 接口也可以配合泛型来使用 已增加灵活性 增强复用性
interface IdFunc<T>{id:(Value:T)=>Tids:()=>T[]
}
let Obj:IdFunc<string>={id(Value){return Value},ids(){return []}
}
泛型类:
class 也可以搭配泛型来用. 比如: react的class组件的基类 Component就是泛型 不用的组件有不同的props和state
//创建泛型类
class GenericNumber<NumType>{defaultvalue: NumType;constructor(value: NumType) {this.defaultvalue = value;}add(x: NumType, y: NumType): NumType {return (x as any) + (y as any);}
}//如果类存在构造函数并且构造函数正好使用到了类的泛型 就可以省略尖括号
联合类型与交叉类型
联合类型的使用和场景
联合类型与类型别名
为了简化复杂的联合类型,可以使用类型别名:
// 定义联合类型别名
type StringOrNumber = string | number;
type Status = "pending" | "approved" | "rejected";let value: StringOrNumber;
value = 123;
value = "hello";let status: Status;
status = "pending"; // 正确
status = "approved"; // 正确
status = "done"; // 错误,不在指定的字面量类型中
联合类型与类型守卫
当使用联合类型时,TypeScript只允许访问所有类型共有的属性和方法。要访问特定类型的属性,需要使用类型守卫:
function processValue(value: string | number) {// 错误:length属性只存在于string类型中// console.log(value.length);// 使用类型守卫if (typeof value === "string") {// 在这个代码块中,TypeScript知道value是string类型console.log(value.length); // 正确console.log(value.toUpperCase());} else {// 在这个代码块中,TypeScript知道value是number类型console.log(value.toFixed(2));}
}
联合类型与接口
联合类型也可以与接口结合使用:
interface Bird {type: "bird";flyingSpeed: number;
}interface Horse {type: "horse";runningSpeed: number;
}// 联合类型
type Animal = Bird | Horse;function moveAnimal(animal: Animal) {switch (animal.type) {case "bird":console.log(`Bird flying at speed: ${animal.flyingSpeed}`);break;case "horse":console.log(`Horse running at speed: ${animal.runningSpeed}`);break;}
}
联合类型与null/undefined
联合类型常用于处理可能为null或undefined的值:
// 用户可能未定义
let user: User | null = null;// 在使用前需要检查
if (user !== null) {console.log(user.name); // 安全访问
}// 或者使用可选链操作符
console.log(user?.name);
联合类型与字面量类型
联合类型与字面量类型结合使用可以创建枚举式的类型:
// 方向只能是这四个字符串值之一
type Direction = "up" | "down" | "left" | "right";function move(direction: Direction) {// ...
}move("up"); // 正确
move("north"); // 错误,不在指定的字面量类型中
联合类型的注意事项
- 只能访问共有成员:使用联合类型时,只能访问所有类型共有的属性和方法
- 类型守卫:要访问特定类型的属性,需要使用类型守卫进行类型检查
- 可读性:对于复杂的联合类型,建议使用类型别名提高可读性
- 过度使用:避免过度使用联合类型,可能导致代码难以维护
交叉类型:
1.定义
使用符合& 对两个接口进行组合 成一个新的类型
交叉功能类似于接口的继承 用来组合多个类型为一个类型(一般用在对象类型中)
//交叉功能类似于接口继承 用于组合多个类型为一个类型(常用于对象类型)
interface Person{name:string
}
interface Contact{phone:number;
}
type PersonContact =Person & Contact;
let obj:PersonContact={name:'张三',phone:123456789
}
2.接口交叉与继承
- 相同点: 都可以实现对象类型的组合
- 不同点:两种方式实现类型组合时 对于同名属性之间处理冲突的方式不同
//交叉类型和接口继承的对比
interface A {fn:(vlaie:number)=>string;
}//接口继承 出现这种情况要么接口会报错 要么只保留一个属性
interface B extends A {fn(value:string):string
}
//交叉类型
interface A { fn:(value:number)=>string;
}
interface B { fn:(value:string)=>string;
}
type C = A & B;
//可以将组合后的c简单理解为 fn:(value:(number|string))=>string
处理方法: 对于接口继承要么类型会报错 要么只保留两个类型的其中之一 然后对于交叉合成来说 可以两个类型同时保留 类似于联合类型
第四部分:类型系统进阶
类型兼容性
结构化类型系统
ts使用的是结构化的类型系统 如果类的类型定义 是一样的 尽管类名是 不一样的 但是仍然可以当做一个类来看
class Point {x:number;y:number;constructor(x:number,y:number) {this.x = x;this.y = y;}
}class Point2D {x:number;y:number;constructor(x:number,y:number) {this.x = x;this.y = y;}
}let p1: Point =new Point2D(1,2) //这种写法是允许的 因为Point2D兼容Point 所以Point和Point2D可以看作是一个类
对象类型兼容
函数类型兼容
类型推断与类型断言
类型推断机制
类型断言的使用场景
映射类型与工具类型
索引签名类型
绝大多数情况下 我们都在使用对象前就确定的对象的结构 但是并未对象添准确的类型 索性签名类型就是为接口中的 索引 和 值 都进行类型标注
使用场景:无法确定对象中有哪些类型信息 此时就用索引签名类型
interface AnyObject{[key:string]:number
}
let obj:AnyObject={a:1,b:2
}
//解释 使用[key:string] 用来约束接口中出现的属性名 表示只要是 string类型 的属性名称都可以出现在对象中
//:number约束了属性值的类型 表示只要是 number类型 的属性值都可以出现在对象中
//key只是一个占位符 有了[key:String]:number 就可以在对象中定义任意个属性 只要属性名是字符串 属性值是数字即可
//这里的key可以是任意名称
- 使用
[key:string]用来约束接口中出现的属性名 表示只要是string类型 的属性名称都可以出现在对象中 :number约束了属性值的类型 表示只要是number类型 的属性值都可以出现在对象中key只是一个占位符 有了[key:String]:number就可以在对象中定义任意个属性 只要属性名是字符串 属性值是数字即可,这里的key可以是任意名称
映射类型
映射类型就是基于旧类型创建新类型(对象类型) 减少重复 提升开发效率
//例子
type Propkeys='x'|'y'|'z'
type Type1={x:number;y:number;z:number;
}
//这样写将x y z重复写了两遍 通常可以使用映射类型来进行简化
type Type2={[Key in Propkeys]:number
}//实际开发还是使用Record类型工具
type Type3=Record<Propkeys,number>;
解释:
- 映射类型是基于索引签名类型的 所以语法类似于索引签名类型 也是用[]
[Key in Propkeys]表示遍历Propkeys中的每个元素 并将其赋值给Key- 映射类型不能用于接口 只能用于类型别名
- 实际开发还是使用
Record泛型工具
对象类型的类型映射
type Props={a:number;b:string;c:boolean;
}
type Type={[key in keyof Props]:number
}
let obj:Type={a:1,b:2,c:3
}
泛型工具类型
Partial<Type>
用来构造一个类型 将type的所有属性设置为可选
-
interface Props{ //每个类型都是必选的属性 如果需要可选类型需要添加'?'id:string;children:number[] } type PartialProps=Partial<Props> // 创建的新类型结构和props一模一样 但是所有属性是可选的const obj0:Props={id:'1',// children:[1,2,3]}//缺少children属性 就会报错const obj:PartialProps={id:'1' }//可以只写一个属性
Readonly<type>
-
创建一个只读的类型 不可更改 就不需要单独为属性添加
readonly属性 -
type readonlyProps=Readonly<Props> const obj1:readonlyProps={id:'1',children:[1,2,3] }obj1.id='2'//不可以修改
Pick<type,keys>
从type中选择一组属性来构造新类型
-
pick中有两个类型变量 如果值选择一个则值传入该属性名即可
-
第二个变量传图的属性只能是第一个类型变量中存在的属性
-
type PickProps=Pick<Props,'id'> const obj2:PickProps={id:'1'//children:[1,2,3]//不可以添加 添加就会报错 }type PickProps = {id: string;}
Record<key,type>
构造一个对象类型 属性键为key 属性类型为type
-
type RecordObj=Record<'a'|'b',string> const obj3:RecordObj={a:'1',b:'2' } //Record工具类型有两个类型变量 1.表示对象有哪些属性 2.表示对象属性对应的类型
第五部分:实用技巧与最佳实践
模块与声明文件
在ts文件中 有两种声明文件的方法 一个是后缀为.ts 一个是后缀为.d.ts
.ts文件 既包含类型信息又包含可执行代码.d.ts文件 只包含类型信息 不包含可执行代码 用途是为js提供类型信息
类型声明文件概述
在开发的时候会使用很多第三方库 我们不知道这些库是用js写的还是ts写的 所以我们需要类型声明文件为已经存在的js库提供类型信息,这样我们在使用这些库的时候就可以获得类型检查和智能提示
使用第三方库的类型声明
可以使用npm i @types/库名 --save来安装库的类型信息(第三方库)
