第一部分:TypeScript基础入门
TypeScript简介
1.什么是TypeScript
2. Ts的优势
- ts是前端项目的首选语言,ts中存在类型推断机制 不需要在代码中的每个地方都显示标注
体验TS与配置
体验ts
-
ts交于js会对数据类型进行检查
-
只需要第一次 定义变量的时候对 数据类型进行注解
let age:number=18;
//:number 是类型注解 表示age变量的类型是number
常见的TS类型注解
-
原始类型
number
string
boolean
symbol
null
undefined
-
对象类型
-
object(数组 对象 函数)
-
联合类型
- 自定义类型(类型别名)
- 接口
- 元组
- 字面量类型
- 枚举
void
any
配置tsconfig.json
第二部分: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, // 值为 0
Down, // 值为 1
Left, // 值为 2
Right // 值为 3
}
// 使用枚举
let dir: Direction = Direction.Up; // 值为 0
console.log(dir); // 输出: 0
可以手动设置枚举成员的值:
// 设置起始值
enum Direction {
Up = 1, // 值为 1
Down, // 值为 2
Left, // 值为 3
Right // 值为 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对象,否则返回null
return 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 private
console.log(student.secret); // Error: Property 'secret' is private
// 3. protected成员 - 不能在类外部访问
console.log(person.age); // Error: Property 'age' is protected
console.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属性 此时就需要为泛型添加约束来收缩类型
有两种为泛型添加约束的方法
多个泛型相互约束:
泛型的类型变量可以存在多个 而且类型变量之间也可以约束的 (比如 第二个类型变量受第一个变量的约束) 比如创建一个函数来获取兑现中属性的值
//泛型的类型变量可以存在多个 而且类型变量之间也可以约束的 (比如 第二个类型变量受第一个变量的约束) 比如创建一个函数来获取兑现中属性的值
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)=>T
ids:()=>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
第五部分:实用技巧与最佳实践
模块与声明文件
在ts文件中 有两种声明文件的方法 一个是后缀为.ts 一个是后缀为.d.ts
-
.ts文件 既包含类型信息又包含可执行代码
-
.d.ts文件 只包含类型信息 不包含可执行代码 用途是为js提供类型信息
类型声明文件概述
在开发的时候会使用很多第三方库 我们不知道这些库是用js写的还是ts写的 所以我们需要类型声明文件为已经存在的js库提供类型信息,这样我们在使用这些库的时候就可以获得类型检查和智能提示
使用第三方库的类型声明
可以使用npm i @types/库名 --save来安装库的类型信息(第三方库)