摸鱼偷学TS:高级类型

浅聊一下

在前面的内容中,我们学习了一些基础类型,其实TypeScript中还有一些高级类型,本篇文章就来总结一下TS中的高级类型

高级类型

交叉类型

交叉类型是将多个类型合并为一个类型。 这让我们可以把现有的多种类型叠加到一起成为一种类型,它包含了所需的所有类型的特性

ts 复制代码
interface IAnyObject {
    [prop:string]:any;
}

function mixin<T extends IAnyObject,U extends IAnyObject>(first:T,second:U): T & U {
    let result = <T & U>{};
    for (let id in first) {
        (<any>result)[id] = first[id];
    }
    for (let id in second) {
        if (!result.hasOwnProperty(id)) {
            (<any>result)[id] = second[id];
        }
    }
    return result;
}

const obj = mixin({a:'juejue'},{b:666});
console.log(obj);//{ a: 'juejue', b: 666 }
  • 创建一个空对象 result,并通过类型断言 <T & U>{} 将其指定为 T & U 类型。这告诉 TypeScript 编译器,虽然 result 最初是空对象,但我们打算将其填充为 T & U 类型的对象。
  • 通过第一个循环,将 first 对象的所有属性复制到 result 对象中。这里使用了类型断言 <any>result 来避免 TypeScript 编译器对属性赋值的严格检查,因为我们知道这样的操作是安全的。
  • 第二个循环遍历 second 对象的属性。如果 result 对象还没有这个属性(通过 hasOwnProperty 检查),则将该属性从 second 复制到 result。这确保了 second 对象的属性不会覆盖 first 对象中已存在的同名属性

看到结果我们可以发现返回的对象拥有着两个对象所有的属性

联合类型

如果你希望一个变量的值是多种类型之一,那么你可以使用联合类型

联合类型表示一个值可以是几种类型之一,我们用竖线(|)分隔每个类型,所以number | string | boolean表示一个值可以是numberstring、或boolean

ts 复制代码
function format(args: string[] | string){
    let res = '';

    if(typeof args === 'string'){
        res = args.trim();
    }
    else{
        res = args.join(' ').trim();
    }
    console.log(res); 
}   
format(['a', 'b']); // a b
format('a b'); // a b

无论我们是传入字符串数组还是字符串都可以很好地调用formate方法

类型别名

类型别名会给一个类型起个新名字,类型别名有时和接口很像,但是可以作用于原始值、联合类型、元组以及其它任何你需要手写的类型.

你可以使用 type SomeName = someValidTypeAnnotation的语法来创建类型别名

ts 复制代码
type some = boolean | string
const b: some = true // ok
const c: some = 'hello' // ok
const d: some = 123 // 不能将类型"123"分配给类型"some"

此类型别名也可以是泛型

ts 复制代码
type Container<T> = {value: T} | {name: string,age : T}
// let o: Container<number> = {value:1}
let o: Container<number> = {name:"juejiue",age:1}

看到这里,发现类型别名怎么跟interface这么像呢?怎么来区别一下?

interface 只能用于定义对象类型,而 type 的声明方式除了对象之外还可以定义交叉、联合、原始类型等,类型声明的方式适用范围显然更加广泛

可辨识联合类型

在开始之前我们要先搞清楚两个概念「类型字面量」与「字面量类型」,因为会在可辨识联合类型的学习中用到类型字面量的特性

字面量类型

字面量(Literal Type)主要分为 真值字面量类型(boolean literal types)、数字字面量类型(numeric literal types)、枚举字面量类型(enum literal types)、大整数字面量类型(bigInt literal types)和字符串字面量类型(string literal types

ts 复制代码
const h1: 123 = 123;
const h2: "123" = "123";
const h3: true = true;
const h4: 0b11 = 3;

字面量类型的值要和实际的值的字面量要一一对应,如果不一致就会报错

当字面量类型与联合类型结合

ts 复制代码
type T1 = 'North' | 'East' | 'South' | 'West';
function move(distance: number, direction: T1) {
    ...
}
move(10, 'North');

此时第二个参数direction就只能传入 'North' | 'East' | 'South' | 'West'其中的一个值

类型字面量

类型字面量不同于字面量类型

ts 复制代码
type Foo = {
  baz: [
    number,
    'xiaomuzhu'
  ];
  toString(): string;
  readonly [Symbol.iterator]: 'github';
  0x1: 'foo';
  "bar": 12n;
};

这不是和接口一模一样吗?有点相似,在一定程度上类型字面量可以代替接口

可辨识联合类型

先来假设一个场景,有两个功能,一个是添加用户,一个是删除用户,添加用户不需要id字段,因为会自动生成,而删除用户需要id字段,于是有以下这段代码

ts 复制代码
interface Info {
    username: string;
}
interface UserAction {
    id?: number;
    action: 'create' | 'delete';
    info: Info;
}

先来创建用户

ts 复制代码
const action: UserAction = {
    id: 10,
    action: 'create',
    info: { username: 'juejue' }
}

我们发现当我们创建用户并且传入一个id时是合法的,但是我们明明不需要这个id,如何解决?

ts 复制代码
type UserAction = {
    id: number
    action: 'delete'
    info: Info
} |
{
    action: 'create'
    info: Info
}

const action: UserAction = {
    // id: 10,
    action: 'create',
    info: { username: 'juejue' }
}

在这里使用类型字面量来解决,如果在创建用户时仍然传入id

似乎完美解决问题,但是如果碰上以下这种情况:

ts 复制代码
const UserReducer = (userAction: UserAction) => {
    console.log(userAction.id);
}

我们不清楚是否具有id属性,这里就会报错了

ts 复制代码
const UserReducer = (user: UserAction) => {
    switch (user.action) {
        case 'delete':
            console.log(user.id);
            break;
        default:
            break;
    }
}

可以使用类型守卫来判断

我们上面提到了 user.action 就是辨识的关键,被称为可辨识的标签,我们发现上面这种模式要想实现必须要三个要素:

  • 具有普通的单例类型属性---可辨识的特征,上文中就是 deletecreate 两个有唯一性的字符串字面量
  • 一个类型别名包含联合类型
  • 类型守卫的特性,比如我们必须用 if switch 来判断 user.action 是属于哪个类型作用域即 deletecreate

结尾

知识点有点复杂,得细心看看...

相关推荐
可夫小子8 分钟前
OpenClaw基础-为什么会有两个端口
前端
喝拿铁写前端15 分钟前
Dify 构建 FE 工作流:前端团队可复用 AI 工作流实战
前端·人工智能
喝咖啡的女孩1 小时前
React 合成事件系统
前端
从文处安1 小时前
「九九八十一难」组合式函数到底有什么用?
前端·vue.js
用户5962585736061 小时前
戴上AI眼镜逛花市——感受不一样的体验
前端
yuki_uix1 小时前
Props、Context、EventBus、状态管理:组件通信方案选择指南
前端·javascript·react.js
老板我改不动了2 小时前
前端面试复习指南【代码演示多多版】之——HTML
前端
panshihao2 小时前
Mac 环境下通过 SSH 操作服务器,完成前端静态资源备份与更新(全程实操无坑)
前端
hulkie2 小时前
从 AI 对话应用理解 SSE 流式传输:一项 "老技术" 的新生
前端·人工智能
dobym2 小时前
里程碑五:Elpis框架npm包抽象封装并发布
前端