TS:好好理解一下type与interface和泛型

33 阅读5分钟

话不多说,直接上干货!!!

type 关键字

type 可以为任意类型创建别名,让代码更简洁、可读性更强,同时能更⽅便地进⾏类型复⽤和扩展。

基本用法

类型别名使⽤ type 关键字定义, type 后跟类型名称,例如下⾯代码中 num 是类型别名。

type num = number;
let price: num
price = 100

联合类型

联合类型是⼀种⾼级类型,它表示⼀个值可以是⼏种不同类型之⼀。

type Status = number | string
type Gender = '男' | '⼥'
function printStatus(status: Status) {
 console.log(status);
}
function logGender(str:Gender){
 console.log(str)
}
printStatus(404);
printStatus('200');
printStatus('501');
logGender('男')
logGender('⼥')

交叉类型

交叉类型(Intersection Types)允许将多个类型合并为⼀个类型。合并后的类型将拥有所有被合并类型的成员。交叉类型通常⽤于对象类型。

//⾯积
type Area = {
 height: number; //⾼
 width: number; //宽
};
//地址
type Address = {
 num: number; //楼号
 cell: number; //单元号
 room: string; //房间号
};
// 定义类型House,且House是Area和Address组成的交叉类型
type House = Area & Address;
const house: House = {
 height: 180,
 width: 75,
 num: 6,
 cell: 3,
 room: '702'
};

interface

interface 是⼀种定义结构的⽅式,主要作⽤是为:类、对象、函数等规定⼀种契约,这样可以确保代码的⼀致性和类型安全,但要注意 interface 只能定义格式,不能包含任何实现 !

基本用法

// 定义类
interface PersonInterface {
 name: string
 age: number
 speak(n: number): void
}

// 定义对象
interface UserInterface {
 name: string
 readonly gender: string // 只读属性
 age?: number // 可选属性
 run: (n: number) => void
}

// 定义函数结构
interface CountInterface {
 (a: number, b: number): number;
}

interface 之间可以继承

interface PersonInterface {
 name: string // 姓名
 age: number // 年龄
}
interface StudentInterface extends PersonInterface {
 grade: string // 年级
}

interface 相同名时自动合并

// PersonInterface接⼝
interface PersonInterface {
 // 属性声明
 name: string
 age: number
}
// 给PersonInterface接⼝添加新属性
interface PersonInterface {
 // ⽅法声明
 speak(): void
}
// Person类实现PersonInterface
class Person implements PersonInterface {
 name: string
 age: number
 // 构造器
 constructor(name: string, age: number) {
 this.name = name
 this.age = age
 }
 // ⽅法
 speak() {
 console.log('你好!我是⽼师:', this.name)
 }
}

泛型

泛型允许我们在定义函数、类或接⼝时,使⽤类型参数来表示未指定的类型,这些参数在具体使⽤时,才被指定具体的类型,泛型能让同⼀段代码适⽤于多种类型,同时仍然保持类型的安全性。

泛型函数

function logData<T>(data: T): T {
 console.log(data)
 return data
}
logData<number>(100)
logData<string>('hello')

泛型函数多个泛型

function logData<T, U>(data1: T, data2: U): T | U {
 console.log(data1,data2)
 return Date.now() % 2 ? data1 : data2
}
logData<number, string>(100, 'hello')
logData<string, boolean>('ok', false)

泛型接口

interface PersonInterface<T> {
 name: string,
 age: number,
 extraInfo: T
}
let p1: PersonInterface<string>
let p2: PersonInterface<number>
p1 = { name: '张三', age: 18, extraInfo: '⼀个好⼈' }
p2 = { name: '李四', age: 18, extraInfo: 250 }

泛型约束

interface LengthInterface {
 length: number
}
// 约束规则是:传⼊的类型T必须具有 length 属性
function logPerson<T extends LengthInterface>(data: T): void {
 console.log(data.length)
}
logPerson<string>('hello')
// 报错:因为number不具备length属性
// logPerson<number>(100)

泛型类

class Person<T> {
 constructor(
 public name: string,
 public age: number,
 public extraInfo: T
 ) { }
 speak() {
 console.log(`我叫${this.name}今年${this.age}岁了`)
 console.log(this.extraInfo)
 }
}
// 测试代码1
const p1 = new Person<number>("tom", 30, 250);
// 测试代码2
type JobInfo = {
 title: string;
 company: string;
}
const p2 = new Person<JobInfo>("tom", 30, { title: '研发总监', company: '发发发
科技公司' });

扩展

1. 函数返回值为 void时的特殊情况

正常情况:

function demo():void{
 // 返回undefined合法
 return undefined
 // 以下返回均不合法
 return 100
 return false
 return null
 return []
}
demo()

特殊情况:

type LogFunc = () => void
const f1: LogFunc = () => {
 return 100; // 允许返回⾮空值
};
const f2: LogFunc = () => 200; // 允许返回⾮空值
const f3: LogFunc = function () {
 return 300; // 允许返回⾮空值
};

原因:是为了确保如下代码成⽴,我们知道 Array.prototype.push 的返回值是⼀个数字, ⽽Array.prototype.forEach ⽅法期望其回调的返回类型是 void 。

const src = [1, 2, 3];
const dst = [0];
src.forEach((el) => dst.push(el));

2. interfacetype的区别

  • 相同点: interface 和 type 都可以⽤于定义对象结构,在定义对象结构时两者可以互换。
  • 不同点:
    • interface: 更专注于定义对象和类的结构,⽀持继承、合并。
    • type :可以定义类型别名、联合类型、交叉类型,但不⽀持继承和⾃动合并。

interface与抽象类的区别

  • 相同点:都能定义⼀个类的格式(定义类应遵循的契约)
  • 不同点:
    • 接⼝:只能描述结构,不能有任何实现代码,⼀个类可以实现多个接⼝。
    • 抽象类:既可以包含抽象⽅法,也可以包含具体⽅法, ⼀个类只能继承⼀个抽象类。

3. extendsinfer

extends

用法1 接口继承: 接口可以通过 extends进行继承

interface Shape {
    color: string;
}

interface Square extends Shape {
    sideLength: number;
}

const square: Square = {
    color: 'blue',
    sideLength: 10
};

用法2 泛型约束: 指定泛型参数必须继承某个类型

interface Lengthwise {
    length: number;
}

function logLength<T extends Lengthwise>(arg: T): T {
    console.log(arg.length);
    return arg;
}

logLength({ length: 10, value: 3 }); // 输出:10

用法3 有条件类型: 有条件的类型会以一个条件表达式进行类型关系检测,从而在两种类型中选择其一

type IsString<T> = T extends string ? 'yes' : 'no';

type T1 = IsString<string>; // 'yes'
type T2 = IsString<number>; // 'no'

infer

现在在有条件类型的 extends 子语句中,允许出现 infer 声明,它会引入一个待推断的类型变量。 这个推断的类型变量可以在有条件类型的true分支中被引用。 允许出现多个同类型变量的 infer 。一般配和 extends 使用。

type FunctionArgumentType<T> = T extends (args: infer U) => any ? U : never;
type ArgumentType = FunctionArgumentType<(args: string) => void>;  //string

4. keyoftypeof

  1. keyof:用来获取某个对象类型的所有健(key),并生成一个联合类型。、
type Person = {
  name: string;
  age: number;
  location: string;
};

type PersonKeys = keyof Person; // "name" | "age" | "location"
  1. typeof:用来获取变量或表达式的类型。可用于运行时获取类型和静态类型检查
// 1. 运行时
let num = 42;
console.log(typeof num); // 输出 "number"

// 2. 静态类型检查
let num = 42;
type NumType = typeof num;  // NumType 被推断为 number
let anotherNum: NumType;    // 这里 anotherNum 的类型就是 number

anotherNum = 24;   // 这是合法的赋值
// anotherNum = "24" // 这会报错,因为字符串不是 number 类型

以上就是本篇文章的全部内容了,如有需要补充的或错误的,请在评论区指正!