TypeScript

121 阅读4分钟

目录

  • 声明文件
  • 类型推论
  • 联合类型
  • 交叉类型
  • 类型断言
  • 内置类型
  • 类型别名
  • keyon in
  • 基本类型
  • interface
  • class
  • 泛型

基础类型

/**************基本数据类型***************/
// 表示任务是否完成的布尔变量
let isTaskCompleted: boolean = false;

// 表示年龄的数字变量
let userAge: number = 10;
  
// 表示用户姓名的字符串变量
let userFirstName: string = 'WangZha';

// 使用模板字符串定义的问候信息字符串变量
let greetingMessage: string = `Hello ${userFirstName}`;

// 未定义类型的变量,值为undefined
let undefinedVar: undefined = undefined;

// null类型的变量
let nullVar: null = null;

// 字符串类型的变量,初始值为null
let userName: string = null;

// 任意类型的变量,初始值为5
let anyVar: any = 5;

// 任意类型可以随意更改,相当于没有设置类型
anyVar = 'maybe a string';

// 更改anyVar的值为布尔值true
anyVar = true;

// 尝试访问anyVar的myName属性,由于anyVar现在是true,所以会引发类型错误
// anyVar.myName;

// 尝试调用anyVar的getName()方法,同样会引发类型错误
// anyVar.getName();

// 数字类型的数组定义
let numbersArray: number[] = [1, 2, 3];

// 在数组末尾添加元素4
numbersArray.push(4);
console.log(numbersArray); // [1,2,3,4]

// 只能在数组中添加数字类型的元素
// numbersArray.push('wangzha'); // 报错

tuple ( 元祖 )

let user: [string,number] = ['wangzha',18]
user = ['wangzhas',20]

function

// 函数定义的几种方式
// 1. 常规函数声明
function addNumber(x: number, y: number, z?: number): number {
    // z 参数是可选的,?:number 表示它是一个可选参数,类型为 number
    return x + y; // 返回两个数字的和
}
addNumber(1, 6); // 调用该函数,因为 z 是可选的,所以这里不需要提供第三个参数

// 2. 箭头函数声明
let addNumber2 = (x: number, y: number) => {
    return x + y; // 返回两个数字的和
}

// 3. 使用具名函数表达式
const addNumber2: (x: number, y: number, t?: number) => number = addNumber; // 这里定义了一个名为 addNumber2 的常量,它是一个函数,该函数接受两个数字和一个可选的数字,并返回一个数字。这个函数被赋值为 addNumber。

// interface
interface FunctionProps {
    // 这是一个方法接口,它描述了一个名为 FunctionProps 的对象应该具有的方法。这个方法接受两个参数:一个数字和一个字符串,并返回一个数字或字符串。
    (x: number, y: string): number | string;
    // name 属性是一个字符串类型。
    name: string;
    // age 属性是一个数字类型。
    age: number;
}

接口 interface

interface是一种定义对象结构的语法糖。它允许你定义对象的形状,并确保对象具有某些属性或方法。

// 定义一个Person接口,包含id、name和可选的age属性
interface Person {
    readonly id: number;
    name: string;
    age?: number;
}

// 创建一个Person对象,包含name、age和id属性
let xiaoming: Person = {
    name: 'xiaoming',
    age: 20,
    id: 1
}

// 定义一个函数sum,接受两个数字参数,返回它们的和
const sum = (x: number, y: number) => {
    return x + y
}

// 定义一个ISum接口,要求实现一个函数,接受两个数字参数并返回数字类型的结果
interface ISum {
    (x: number, y: number): number
}

// 将sum函数赋值给sum2变量,使sum2满足ISum接口的要求
const sum2: ISum = sum

// 定义一个RandomMap接口,要求实现一个对象,其属性名是字符串类型,属性值也是字符串类型
interface RandomMap {
    [propName: string]: string;
}
// 创建一个RandomMap对象,包含a、b和c属性,分别赋值为hello、test和test
const test: RandomMap = 
    a: 'hello',
    b: 'testb',
    c: 'testc'
}

// 定义一个LikeArray接口,要求实现一个对象,其索引是数字类型,值是字符串类型
interface LikeArray {
    [index: number]: string
}

// 创建一个LikeArray对象,包含1、2和3属性,分别赋值为'1'、'2'和'3'
const likeArray: LikeArray = ['1', '2', '3']
  
// duck typing(鸭子类型):只检查对象是否有需要的属性或方法,而不关心这些属性的具体定义是什么。这是通过类型断言实现的。
// 定义一个FunctionWithProps接口,要求实现一个函数和一个name属性。函数接受一个数字参数并返回数字类型的结果。name属性是字符串类型。
interface FunctionWithProps {
    (x: number): number;
    name: string;
}
// 创建一个FunctionWithProps对象,包含一个函数和一个name属性。函数接受一个数字参数并返回该参数。name属性被赋值为'abc'。注意这里没有显式地声明函数返回值类型,TypeScript会自动推断出返回值类型为number。同时也没有显式地声明name属性类型,TypeScript会自动推断出name属性类型为string。这就是duck typing的特性。
const a: FunctionWithProps = (x: number) => {
    return x
}
a.name = 'abc'

泛型 <>

泛型是一种让用户定义可重用的组件,而不需要为各种数据类型编写重复的代码的特性。通过使用泛型,您可以在编写代码时指定一个或多个类型参数,这些类型参数在组件的整个生命周期中都会被保留。

/************************* 基础泛型 *************************/
// 定义一个泛型函数 echo,它接受一个类型为 T 的参数 arg,并返回一个类型也为 T 的结果。
function echo<T>(arg: T): T {
    return arg;
}
// 使用 echo 函数,传入字符串 'xiaoming',并将返回的结果赋值给常量 each。
const each = echo('xiaoming');

// 定义一个泛型函数swap,它接受一个由两个类型分别为T和U的元素组成的元组,并返回一个元组,其元素顺序与输入元组相反。
function swap <T, U>(tuple: [T, U]): [U, T] {
    // 使用数组解构的方式来颠倒元组中的元素顺序。
    return [tuple[1], tuple[0]];
}
// 调用swap函数,并传入一个元组['string', 123]。此元组的类型为[string, number]。
// 函数会返回一个新的元组[123, 'string'],其类型为[number, string]。
const result = swap(['string', 123]); // 正确的调用方式

// 定义一个名为 CountryResp 的接口,用于描述国家响应数据的结构
interface CountryResp {
    name: string; // 国家的名称,数据类型为字符串
    area: number; // 国家的面积,数据类型为数字
    population: number; // 国家的人口数量,数据类型为数字
}

// 定义一个泛型函数 withAPI,它接受一个 URL 字符串作为参数,并返回一个 Promise 对象
// T 是泛型参数,表示这个函数可以处理任何类型的数据
function withAPI<T>(url: string): Promise<T> {
// 使用 fetch API 从给定的 URL 获取数据,然后将其解析为 JSON 格式
// 最后返回解析后的 JSON 数据(类型为 T)的 Promise 对象
    return fetch(url).then(resp => resp.json())
}

// 调用 withAPI 函数,并指定返回数据的类型为 CountryResp
// 传入 'country.resp' 作为 URL 参数来获取国家数据
withAPI<CountryResp>('country.resp').then(resp => {
    // 在这里处理获取到的国家数据
    // 例如,打印出国家的名称
       console.log(resp.name);
})

/************************* 类中的泛型 *************************/
class Matrix<T> {
    private elements: T[][] = [];
    constructor(elements: T[][]) {
    this.elements = elements;
    }
    getElement(i: number, j: number): T {
        return this.elements[i][j];
    }
    setElement(i: number, j: number, element: T) {
        this.elements[i][j] = element;
    }

}
// Create a matrix of strings.
const matrix1 = new Matrix<string>([["a", "b"], ["c", "d"]]);
/************************* 泛型约束 *************************/
// 关键字:extends
// extends关键字用于表示一个类继承另一个类。
// 通过使用extends,子类可以继承父类的属性和方法,并且可以添加或覆盖父类的实现。
interface IWithLength {
    length:number
}
// extends in generice
function echoWithArr<T extends IWithLength>(arg:T):T {
    console.log(arg.length);
    return arg
}
const arrs = echoWithArr([1, 2, 3])
const str = echoWithArr('123')
const obj = echoWithArr({length: 123, width: 'hello' })

// 定义一个泛型类型 NonType,它接受一个类型参数 T。
// 如果 T 是 null 或 undefined,则 NonType 的类型是 never;否则,NonType 的类型就是 T。
type NonType<T> = T extends null | undefined ? never : T;
// 定义一个变量 demo1,其类型为 NonType<number>。
// 因为 number 不是 null 或 undefined,所以 demo1 的类型是 number。
let demo1: NonType<number>;
// 定义一个变量 demo2,其类型为 NonType<null>。
// 因为 null 是 null 或 undefined,所以 demo2 的类型是 never。
let demo2: NonType<null>;

图片.png 图片.png 图片.png

类 class

/************** es 类的基础使用***************/
// 定义一个动物类
// 包含受保护的动物名称属性
class Animal {
    // 受保护的动物名称,只能在Animal类内部和继承Animal类的子类中访问
    protected animalName: string;
    // 构造函数,接收一个动物名称参数,并将其赋值给animalName属性
    constructor(animalName: string) {
        this.animalName = animalName;
    }

![图片.png](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/b9feae96490f46308b149be5501b7b91~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1866&h=880&s=348564&e=png&b=292a25)
    // 定义一个run方法,返回一个字符串,表示动物正在奔跑
    run() {
        return `${this.animalName} is running`;
    }

}
// 创建一个Animal类的实例,名为snake,并传入Lily作为参数
const snake = new Animal('Lily');
// 调用snake对象的run方法,输出Lily is running
snake.run();

// 定义一个Dog类,继承自Animal类
class Dog extends Animal {
    // 定义一个bark方法,返回一个字符串,表示狗正在吠叫
    bark() {
        return `${this.animalName} is barking`;
    }
}
// 创建一个Dog类的实例,名为xiaobao,并传入Xiaobao作为参数
const xiaobao = new Dog('Xiaobao');
// 调用xiaobao对象的run方法,输出Xiaobao is running
xiaobao.run();
// 定义一个Cat类,继承自Animal类
class Cat extends Animal {
    // 重写Animal类的构造函数,接收一个catName参数,并调用父类的构造函数,同时输出animalName的值
    constructor(catName: string) {
        super(catName);
        console.log(this.animalName);
    }
    // 定义一个run方法,返回一个字符串,表示猫正在发出“Meow”的声音,并调用父类的run方法
    run() {
        return 'Meow, ' + super.run();
    }
}
// 创建一个Cat类的实例,名为maomao,并传入Maomao作为参数
const maomao = new Cat('Maomao');

/************** ts 中使用 ***************/
//implements关键字用于表示一个类实现了某个接口。接口是一种定义对象结构的方式,而类则是一种实现这种结构的实例。通过使用 `implements` 关键字,你可以确保类遵循特定接口的所有要求和属性。
interface TimerInterface {
    currentCountdown: number;
    startCountdown(): void;
}
interface TimerStatic {
    new(seconds: number): void;
    countdownTime: number;
}
interface GameInterface {
    play(): void;
}
const Timer: TimerStatic = class Timer implements TimerInterface {
    constructor(seconds: number) {
        this.currentCountdown = seconds;
    }
    static countdownTime = 120; // 120秒倒计时,可根据实际需要进行修改
    currentCountdown: number = 0; // 初始化为0,可以在构造函数中进行计算和设置
    startCountdown() {
        // 启动倒计时逻辑(可以在此处添加具体实现)
    }
}
class GameDevice implements TimerInterface, GameInterface {
    currentCountdown: number = 0; // 初始化为0,可以在构造函数中进行计算和设置
    startCountdown() {
        // 启动倒计时逻辑(可以在此处添加具体实现)
    }
    play() {
        // 播放游戏音效或启动游戏逻辑(可以在此处添加具体实现)
    }
}

类型别名

在 TypeScript 中,类型别名(Type Aliases)是一种用于给复杂类型命名的机制。类型别名可以让代码更加简洁和可读,并使类型定义更加模块化和可重用。

要定义一个类型别名,可以使用 type 关键字,后面跟上别名名称和类型声明

// 类型别名 - 为sum变量定义了一个类型别名,该类型别名表示一个接受两个数字参数并返回一个数字的函数  
let sum: (x: number, y: number) => number  
// 使用sum函数计算1+2的结果,并将结果存储在result变量中  
const result = sum(1,2)  
  
// 类型别名 - 定义了一个类型别名addType,它表示一个接受两个数字参数并返回一个数字的函数  
type addType = (x: number, y: number) => number  
// 为sum2变量定义了PlusType类型别名  
let sum2: addType  
// 使用sum2函数计算1+2的结果,但不将结果存储在任何变量中  
sum2(10, 12)

联合类型 |

let numberOfString: number | string;

交叉类型 &

interface PersonName {
    name:string
}
type PersomOrAge = PersonName & {age:number}
let person:PersomOrAge = {
    name:'wangzha',
    age:19
}

类型断言 as

function getLength(input: number | string) {
  const str = input as string
  if (str.length) {
    return str.length
  } else {
    const number = input as number
    return number.toString().length
  }
}

高级用法 keyof in

keyof 是一个类型操作符,用于获取一个类型的所有键(属性名)组成的联合类型。keyof 可以用于对象类型、接口类型或类型别名。

keyof 与映射类型:

// 定义一个名为 CountryResp 的接口,用于描述国家的信息 interface CountryResp { name: string; // 国家名称,字符串类型 area: number; // 国家面积,数字类型 population: number; // 国家人口,数字类型 }

// 使用 keyof 操作符获取 CountryResp 接口的所有属性名,组成一个新的类型 Keys // 这里的 Keys 将是一个只包含 'name', 'area', 'population' 的字符串字面量类型 type Keys = keyof CountryRes

// 使用映射类型 (mapped types) 创建一个新的类型 NameType,其属性名与 CountryResp 相同,但值类型为 NameType['name'] type NameType = { [P in Keys]?: CountryResp[P] extends string ? P : never }[Keys]

// 使用映射类型创建一个新的类型 Test,其属性名与 Keys 相同,但值类型为 any,意味着不限制属性的具体类型 type Test = { [key in Keys]: any }

// 使用可选链和映射类型创建一个新的类型 CountryOpt,其属性名与 Keys 相同,但值类型为可选的 CountryResp 属性类型 type CountryOpt = { [p in Keys]?: CountryResp[p] | undefined }


![图片.png](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e735091f7179471cb6a54315bf4a434b~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=2062&h=1328&s=489546&e=png&b=282924)

## React 函数组件源码分析

![图片.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/81f2b32054ff454b97d3609981fd42ed~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=2315&h=1235&s=894609&e=png&b=242520)



## 定义一个 index.d.ts 文件
将定义好的 `index.d.ts` 放置到 `node_modules/@types/xxx文件名/index.d.ts`

```typescript
type HTTPMethod = 'GET' | 'POST' | 'PATCH' | 'DELETE'
declare function myFetch<T = any>(url: string, method: HTTPMethod, data?: any) : Promise<T>

declare namespace myFetch {
  const get: <T = any>(url: string) => Promise<T>;
  const post: <T = any>(url: string, data: any) => Promise<T>;
}
export = myFetch

备注:以上内容参考于慕课网架构师系列教程