Typescript 高级语法总结

542 阅读6分钟

记录了一些常用的ts知识点,也是学习的点滴记录,大佬直接跳过。

类型谓词

类型谓词 类型谓词是一种特殊的返回类型,它向Typescript编译器发出一个特定值是什么类型的信号。类型谓词总是附加到接受单个参数并返回布尔值的函数。类型谓词表示为 argumentName is Type

让我们看个例子

interface Bird {
    name: string
    age: number
}

interface Fish {
    love: string
    age: number
}

function getSmallPet(): Bird | Fish {
    // ...
    return {
        love: '小龙虾',
        age: 24
    }
}

let pet = getSmallPet();

if ((pet as Bird).name) {
    console.log((pet as Bird).name)
} else {
    console.log((pet as Fish).love);
}

这个例子里, Bird具有一个 name成员。 我们不能确定一个 Bird | Fish类型的变量是否有 name方法。 如果变量在运行时是 Fish类型,那么调用 pet.name就出错了所以我们需要断言(pet as Bird) 告诉代码肯定有这个成员

断言另一种写法

if ((<Bird>pet).name) {
    console.log((<Bird>pet).name)
} else {
    console.log((<Fish>pet).love);
}

这里可以注意到我们不得不多次使用类型断言。 假若我们一旦检查过类型,就能在之后的每个分支里清楚地知道 pet的类型的话就好了。

TypeScript里的 类型保护机制让它成为了现实。 类型保护就是一些表达式,它们会在运行时检查以确保在某个作用域里的类型。 要定义一个类型保护,我们只要简单地定义一个函数,它的返回值是一个 类型谓词

function isFish(pet: Fish | Bird): pet is Bird {
    return (<Bird>pet).name !== undefined;
}

// 这样调用就都不报错了,简化了类型断言
if (isFish(pet)) {
    console.log(pet.name);
} else {
    console.log(pet.love)
}

在这个例子里, pet is Fish就是类型谓词。 谓词为 parameterName is Type这种形式, parameterName必须是来自于当前函数签名里的一个参数名。

keyof 和 typeof

keyopf keyof与Object.keys略有相似,只是 keyof 是取 interface 的键,而且 keyof 取到键后会保存为联合类型。

interface iUserInfo { 
    name: string; 
    age: number; 
} 
type keys = keyof iUserInfo;
// 输入内容是 'name' | 'age'

ok,举个例子

我们有这样一个需求,实现一个函数 getValue 取得对象的 value。在未接触 keyof 时,我们一般会这样写:

function getValue(o: object, key: string) {
  return o[key];
}
const obj1 = { name: '张三', age: 18 };
const name = getValue(obj1, 'name');

但是这样有2个问题:

  • 无法确定返回值类型
  • 无法对 key 进行约束,可能会犯拼写的错误也检测不到 这时我们可以使用 keyof 来增强 getValue 函数的类型功能。
  function getValue<T extends Object , K extends keyof T>(o: T, key: K): T[K] {
    return o[key];
  }
  
  const obj1 = { name: '张三', age: 18 };
  const a2 = getValue(obj1, 'name');
  // 这样getvalue第二个参数只能是name或者age,如果输入其他则会报错

还有另一种写法

这种写法就是控制参数的类型,推荐上面那种泛型方法

function getValue(o: objType, key: keyof objType): objType[keyof objType] {
    return o[key];
}
  
const obj1 = { name: '张三', age: 18, sex: 2 };
type objType = typeof obj1
const a2 = getValue(obj1, 'name');

typeof 在 TypeScript 中,typeof 操作符可以用来获取一个变量或对象的类型。

const sem = { name: "semlinker", age: 30 };
type Sem = typeof sem; // type Sem = { name: string, age:number }

typeof操作符除了可以获取对象的结构类型之外,它也可以用来获取函数对象的类型

function toArray(x: number): Array<number> {
  return [x];
}

type Func = typeof toArray; // -> (x: number) => number[]

const断言

TypeScript 3.4 引入了一种新的字面量构造方式,也称为 const 断言。

  • 表达式中的任何字面量类型都不应该被扩展;
  • 对象字面量的属性,将使用 readonly 修饰;
  • 数组字面量将变成 readonly 元组。 下面我们来举一个 const 断言的例子:
let x = "hello" as const;
type X = typeof x; // type X = "hello"

let y = [10, 20] as const;
type Y = typeof y; // type Y = readonly [10, 20]

let z = { text: "hello" } as const;
type Z = typeof z; // let z: { readonly text: "hello"; }

z.text = 'you' // error 只读属性 不可改变

in

in用于取联合类型的值。主要用于数组和对象的构造。

type name = 'firstName' | 'lastName';
type TName = {
  [key in name]: string;
};

image.png

in还可以结合泛型使用,看下面例子

type Tperson<T> = {
    [key in keyof T]: string | number
}
const person = {name: '孙悟空', age: 18, sex: '男'};
const obj:Tperson<typeof person> = {name:'', age:2,sex:''}

image.png

泛型工具

Partial<T>

此工具的作用就是将泛型中全部属性变为可选的。

type Animal = {
    name: string,
    category: string,
    age: number,
    eat: () => number
}
type PartialAnimal = Partial<Animal> 
/**
    type PartialAnimal = {
        name?: string;
        category?: string;
        age?: number;
        eat?: () => number;
    }
*/
const cc:PartialAnimal = { name: '孙悟空' } // 属性全部可选后,可以只赋值部分属性

Pick<T, K>

工具的作用是将 T 类型中的 K 键列表提取出来,生成新的子键值对类型。k就是制定留下来哪些类型

type Animal = {
    name: string,
    category: string,
    age: number,
    eat: () => number
}
type BridType = Pick<Animal, "name" | "age">
const bird: BridType = { name: 'bird', age: 1, } // brid只能有name和age属性

Omit<T, K>

正好与Pick相反,它是去除掉某个属性。Pick是留下来某个属性 还是以上面的Animal为例

const OmitAnimal:Omit<Animal, 'name'|'age'> = { 
    category: 'lion', 
    eat: () => { return 1 } 
} // 必须实现category和eat

Exclude<T, U>

此工具是在 T 类型中,去除 T 类型和 U 类型的交集,返回剩余的部分。

注意这里的 extends 返回的 T 是原来的 T 中和 U 无交集的属性,U属性不会返回,不会返回!!!

type T1 = Exclude<'a' | 'b', 'c' | 'a' | 'd'> // 'b'

type T2 = Exclude<string | number, string> // number

type T3 = Exclude<string, string | number | boolean> // '' 并不会返回U属性

ReturnType<T>

此工具就是获取 T 类型(函数)对应的返回值类型:

type foo = (x: string | number)=> string | number 
function func2() {
    return '悟空'
}

type fooT = ReturnType<foo> // string | number
type get = ReturnType<typeof func2> // string

Required<T>

此工具可以将类型 T 中所有的属性变为必选项。 举个例子

type person = {
    name: string
    age?: number
    sex?: string
}
const xiaoming:Required<person> = {
    name: 'ws',
    age: 20,
    sex: '男'
}

看上面代码,虽然age和sex属性是可选属性,但是经过Required处理后,变为了必选属性

typescript封装LocalStorage并支持过期时间

enum ts 定义枚举

//字典 Dictionaries    expire过期时间key    permanent永久不过期
export enum Dictionaries {
    expire = '__expire__',
    permanent = 'permanent'
}

type ts 定义类型

import { Dictionaries } from "../enum"
export type Key = string //key类型
export type expire = Dictionaries.permanent | number //有效期类型
export interface Data<T> {  //格式化data类型
    value: T
    [Dictionaries.expire]: Dictionaries.expire | number
}
export interface Result<T> { //返回值类型
    message: string,
    value: T | null
}
export interface StorageCls { //class方法约束
    set: <T>(key: Key, value: T, expire: expire) => void
    get: <T>(key: Key) => Result<T | null>
    remove: (key: Key) => void
    clear: () => void
}

index.ts 主要逻辑实现

import { StorageCls, Key, expire, Data,Result } from "./type";
import { Dictionaries } from "./enum";
export class Storage implements StorageCls {
    //存储接受 key value 和过期时间 默认永久
    public set<T = any>(key: Key, value: T, expire: expire = Dictionaries.permanent) {
    //格式化数据
        const data = {
            value,
            [Dictionaries.expire]: expire
        }
        //存进去
        localStorage.setItem(key, JSON.stringify(data))
    }
 
    public get<T = any>(key: Key):Result<T | null> {
        const value = localStorage.getItem(key)
        //读出来的数据是否有效
        if (value) {
            const obj: Data<T> = JSON.parse(value)
            const now = new Date().getTime()
            //有效并且是数组类型 并且过期了 进行删除和提示
            if (typeof obj[Dictionaries.expire] == 'number' && obj[Dictionaries.expire] < now) {
                  this.remove(key)
                  return {
                     message:`您的${key}已过期`,
                     value:null
                  }
            }else{
            //否则成功返回
                return {
                    message:"成功读取",
                    value:obj.value
                }
            }
        } else {
           //否则key值无效
            console.warn('key值无效')
            return {
                message:`key值无效`,
                value:null
             }
        }
    }
    //删除某一项
    public remove(key:Key) {
        localStorage.removeItem(key)
    }
    //清空所有值
    public clear() {
       localStorage.clear()
    }
}