ts类型,泛型,映射等基础知识简记

789 阅读6分钟

ts数据类型:

整理自ts系列

boolean

let bool:boolean = true;

string

let str:string = "string";

number

let num:number = 0;

array

let numbers:number[] = [1,2,3,4,5];
// number|string代表联合类型, 下面的高级类型中会讲
let numbers:(number|string)[] = [1,2,3,4,'5'];
// 通过泛型表示, Array<元素类型>
let numbers:Array<number> = [1,2,3,4,5];

元组(Tuple)

// 元组类型,表示一个已知元素数量和类型的数组, 各元素的类型可以不同:
let list1:[number, string] = [1, '2', 3]; // 错误, 数量不对, 元组中只声明有2个元素
let list2:[number, string] = [1, 2]; // 错误, 第二个元素类型不对, 应该是字符串'2'
let list3:[number, string] = ['1', 2]; // 错误, 2个元素的类型颠倒了
let list4:[number, string] = [1, '2']; // 正确

enum

any

undefined

所有类型的子类型
let str:undefined = "string"

null

所有类型的子类型
let num:null = 123

void

void的意义和any相反, 表示不是任何类型, 一般出现在函数中, 用来标记函数没有返回值:
function abc(n:number):void{
    console.log(n);
}

never

never表示不可达,主要使用在`throw`的情况下:
function error():never{
    throw '错了!';
}

其中,object包含:enum array tuple

接口(interface)

一种定义复杂类型的格式, 比如我们用对象格式存储一篇文章, 那么就可以用接口定义文章的类型:

interface Article {
    title: string;
    count: number;
    content:string;
    fromSite: string;
}

const article: Article = {
    title: '为vue3学点typescript(2), 类型',
    count: 9999,
    content: 'xxx...',
    fromSite: 'baidu.com'
}

通过基础类型组合而来的, 我们可以叫他高级类型. 包含: 交叉类型 / 联合类型 / 接口等等

用接口定义函数

接口不仅可以定义对象, 还可以定义函数:

// 声明接口
interface Core {
    (n:number, s:string):[number,string]
}

// 声明函数遵循接口定义
const core:Core = (a,b)=>{
    return [a,b];
}

类型别名(type)

类型别名可以表示很多接口表示不了的类型, 比如字面量类型(常用来校验取值范围):

type A = 'top'|'right'|'bottom'|'left'; // 表示值可能是其中的任意一个
type B = 1|2|3;
type C = '红'|'绿'|'黄';
type D = 150;

let a:A = 'none'; // 错误, A类型中没有'none'
更多组合
interface A1{
    a:number;
}
type B = A1 | {b:string};
type C = A1 & {b:string};

// 与泛型组合
type D<T> = A1 | T[];

命名空间(namespace)

如果你发现自己写的功能(函数/类/接口等...)越来越多, 你想对他们进行分组管理就可以用命名空间, 比如"":

namespace Tools {
    const TIMEOUT:number = 100;
    export class Ftp {
        construtor() {
            setTimeout(() => {
                console.log("test")
            }, TIMEOUT)
        }
     }
     export function parseURL() {
         console.log("parseURL")
     }
}

export 是表示外部可以访问

Tools.TIMEOUT // error Tools上没有这个属性
Tools.parseURL() // parseURL

常用的就是用namesapce管理类型

namespace Food {
    export type A = "蔬菜“ | ”水果" | "海鲜"
    export interface Fruits {
        taste: string,
        price: number,
    }
    export interface Meat {
        taste: string,
        price: number,
    }
}
let classif: Food.A
let fruits: Food.Fruits

引入写好的命名空间

方法一:

< reference path="./xxx.ts">

方法二:

import {xxx} from "xxx.ts"

声明(declare)

全局声明要以文件d.ts结尾

// global.d.ts
declare let s: string
declare interface Food {
    (taste: string) : string
}
// app.ts
s = "sweet"
Food(s)

模块声明

// module.ts
declare module interface Meat {
    (taste: string): void
}
// app.ts
const meat = require("./module.ts")
meat.Meat = ("salty") => console.log("salty")

交叉类型(&)

交叉类型是将多个类型合并为一个类型, 表示"并且"的关系,用&连接多个类型, 常用于对象合并:

interface A {a:number};
interface B {b:string};

const a:A = {a:1};
const b:B = {b:'1'};
const ab:A&B = {...a,...b};

联合类型(|)

联合类型也是将多个类型合并为一个类型, 表示""的关系,用|连接多个类型:

function setWidth(el: HTMLElement, width: string | number) {
    el.style.width = 'number' === typeof width ? `${width}px` : width;
}

"泛型变量"和"泛型"

变量的概念我们都知道, 可以表示任意数据, 泛型变量也一样, 可以表示任意类型:

// 在函数名后面用"<>"声明一个泛型变量
function convert<T>(input:T):T{
    return input;
}

在类名后面通过"<>"声明一个泛型变量, 类的方法和属性都可以用这个泛型, 接下来我们使用下泛型类:

let a =  new Person<string>('詹姆斯邦德');
a.say(007) // 错误, 会提示参数应该是个string
a.say('007') // 正确

泛型类型

我们可以用泛型变量去描述一个类型(类型范围), ts的数组类型Array本身就是一个泛型类型, 他需要传递具体的类型才能变的精准:

let arr : Array<number>;
arr = ['123']; // 错误, 提示数组中只可以有number类型
arr = [123];

泛型接口

通过传入不同的类型参数, 让属性更灵活:

interface Goods<T>{
    id:number;
    title: string;
    size: T;
}

let apple:Goods<string> = {id:1,title: '苹果', size: 'large'};
let shoes:Goods<number> = {id:1,title: '苹果', size: 43};

泛型约束

function echo<T>(input: T): T {
    console.log(input.name); // 报错, T上不确定是否由name属性
    return input;
}

T可以代表任意类型, 但对应的都是基础类型, 所以当我们操作input.name的时候就需要标记input上有name属性, 这样就相当于我们缩小了泛型变量的范围, 对泛型进行了约束:

// 现在T是个有name属性的类型
function echo<T extends {name:string}>(input: T): T {
    console.log(input.name); // 正确
    return input;
}

自动类型推断(不用你标类型了,ts自己猜)

ts会根据定义赋值自动判断类型,还会根据浏览器自带的api,比如typeof / instanceof自动做类推断

类型断言(你告诉ts是什么类型, 他都信)

有些情况下系统没办法自动推断出正确的类型, 就需要我们标记下, 断言有2种语法, 一种是通过" <> ", 一种通过"as", 举例说明:

let obj = 0.5 < Math.random() ? 1 : [1]; // number|number[]

// 断言, 告诉ts, obj为数组
(<number[]>obj).push(1);

//等价
(obj as number[]).push(1);

索引类型(keyof)

js中的Object.keys()和ts中keyof类似,用来获取ts对象类型中的key值

type A = keyof {a:1,b:'123'} // 'a'|'b'
type B = keyof [1,2] // '0'|'1'|'push'... , 获取到内容的同时, 还得到了Array原型上的方法和属性(实战中暂时没遇到这种需求, 了解即可)

type类型可以获得键值,也可以获得对象类型的值

type A = {a: 1, b: "123"}
type B = A['a']
let b:B = 2 // error b只能等于1

映射类型

映射类型就是修改类型的函数工具方法

Partial< T>就是让所有属性变成可选的
interface A = {
   a: string,
   b: number,
}
// {a?: string, b?: number}
interface A1 = Partial<A> 

Required< T>让所有属性变成必选

type A = { a?: string, b?: string }
type A1 = Required<A> // {a: string, b: string}

Pick< T,K> 保留自己选择的属性,K表示想要保留的属性键值(是键值key!!)

type A = {a: number, b: string, c: boolean}
type A1 = Pick<A, 'a'|'b'> // {a: number, b: string}

Omit< T> 排除已选的属性

type A = {a: string, b: number}
type A1 = Omit<A, 'a'> // {b: number}

Record< K,T> 创建一个类型,K是key的类型,T是值的类型

type A = Record<string, number> // {[key: string]: number}

Exclude< T,U> 将T类型中,有和U类型相同的类型过滤掉

type A1 = Exclude<number|string, string|number[]> // number

// 兼容
type A2 = Exclude<number|string, any|number[]> // never , 因为any兼容number, 所以number被过滤掉

Extract< T,U> 提取T和U中相同的类型

type A1 = Extract<number|string, string|number[]> // string

Parameters 获取函数参数类型

interface fun = {
    (a: number, b: string): string|number
}
type A = Parameters<fun> // [number, string]

extends 条件类型

Z extends T ? X : Y

如果Z的类型是T,那么就是返回X,否则返回Y

type A = string extends '123' ? string : '123' // '123'
type b = '123' extends string ? string : '123' // string