Typescript入门 | 青训营笔记

59 阅读6分钟

这是我参与「第四届青训营 」笔记创作活动的第9天

Typescript入门

一.为什么需要Typescript

  • Javascript

    • 动态类型
    • 弱类型语言
  • Typescript

    • 静态类型

      • 可读性增强:基于语法解析TSDoc,ide增强
      • 可维护性增强:在编译阶段暴露大部分错误 - > 多人合作的大型项目中,获得更好的稳定性和开发效率
    • 弱类型语言

    • Javascript的超集

      • 包含于兼容所有Js特性,支持共存
      • 支持渐进式引入与升级

二.基本语法

1.基础数据类型

语法 const 变量名: 数据类型 = 值

  • javascript
//字符串
const q ="string";
//数字
const w = 1;
//布尔值
const e = true;
//null
const r = null;
//undefined
const t = undefined;
  • typrscript
//字符串
const q: string ="string";
//数字
const w: number = 1;
//布尔值
const e: boolean = true;
//null
const r: null = null;
//undefined
const t: undefined = undefined;

2.对象类型

//先声明一个自定义类型对象,按照约定,统一首字母大写
//使用interface
const interface IBytedancer {
    //只读属性(readonly):约束属性不可在对象初始化外赋值
    readonly jobId: number;
    name: string;
    sex: 'man'|'woman'|'other';
    age: number;
    //可选属性:定义该属性可以不在
    bobby?: string;
    //任意属性:约束所有对象属性都必须是该属性的子类型
    [key: string]: any;
}
//对象初始化
const bytedancer: IBytedancer = {
    jobId:341200,
    name:'yuan',
    sex:'woman',
    age:198,
    hobby:'swimming',
}
​
//-----------------------------//报错:无法分配到‘jobId’,因为它只是只读属性
bytedancer.jobId = 123456;
//成功:任意属性标注下可以添加任意属性
bytedancer.plateform = 'data';
//报错:缺少属性‘name’,hobby可缺省
const bytedancer2: IBytedancer = {
    jobId: 99999,
    sex:'man',
    age;18,
}

3.函数类型

  • javascript
function add(x,y) {
    return x + y;
}
​
const mult = (x,y) => x*y;
  • typescript
function add(x: number,y: number): number {
    return x + y;
}
​
const mult: (x: number,y: number) => number = (x,y) => x*y;
​
//----------------------interface IMult {
    (x: number,y: number): number;
}
const mult: IMult = (x,y) => x*y;

注:给每个参数添加类型之后,可以不用给函数本身添加返回值类型,因为TypeScript能根据返回语句自动推断出返回值类型

4.函数重载

function getDate(type: 'string',timestamp?: string): string;
function getDate(type: 'date',timestamp?: string): Date;
function getDate(type: 'string' |' date', timestamp?: string): string | Date {
    const date = new Date(timestamp);
    return type === 'string' ? date.toLocaleString() : date;
}
​
const x =getDate('date'); //x: Date
const y =getDate('string','2018-01-10'); //y: string
//对getDate函数进行重载,timestamp为可缺省参数
function getDate(type: 'string',timestamp?: string): string;
interface IGetDate {
    (type: 'string', timestamp?: string): string;
    (type: 'date', timestmap?: string): Date;
    (type: 'string' |' date', timestamp?: string): string | Date;
}
​
//注意:不能将 “(type: any,timestamp: any) => string | Date”分配给类型“IGetDate”。
//不能将类型“string | Date” 分配给类型“string”
//不能将类型“Date”分配给类型“string”。
const getDate2: IGetDate = (type, timestamp) => {
    const date = new Date(timestamp);
    return type === 'string' ? date.toLocaleString() : date;
}

5.数组类型

// 类型 + 方括号 
type IArr1 = number[];
//泛型表示
type IArr2 = Array<string | number | Record<string,number>>
//元祖表示
type IArr3 = [number,number,string,string];
//接口表示
interface IArr4 {
    [key: number] any;
}
​
const arr1: IArr1 = [1,2,3,4,5,6];
const arr2: IArr2 = [1,2,'3','4',{a: 1}];
const arr3: IArr3 = [1,2,'3','4'];
const arr4: IArr4 = ['string',() => null,{},[]];

6.Typescript补充类型

//空类型,表示无赋值
type IEmptyFunction = () => void;
//任意类型,是所有类型的子集
type IAnyType = any;
//枚举类型:支持枚举值到枚举名的正、反向映射
enum EnumExample {
    add = '+',
    mult = '*',
}
EnumExample['add'] === '+';
EnumExample['+'] === 'add';
​
enum ECorlor {Mon,Tue,Wed,Thu,Fri,Sat,Sun};
ECorlor['Mon'] === 0;
ECorlor[0] === 'Mon';
//泛型
type INumber = Array<number>;

7.Typescript泛型

function getRepeatArr(target) {
    return new Array(100).fill(target);
}
​
type IGetRepeatArr = (target: any) => any[];
​
//不预先指定具体的类型,而在使用的时候再指定类型的一种特性
type IGetRepeatArrR = <T>(target: T) => T[];
//泛接口 & 多泛型
interface IX<T, U> {
    key: T;
    val: U;
}
​
//泛型类
class IMan<T> {
    instance: T;
}
​
//泛型别名
type ITypeArr<T> = Array<T>;
//泛型约束:限制泛型必须符合字符串
type IGetRepeatStringArr = <T extends string>(target: T) => T[];
const getSteArr: IGetRepeatStringArr = target => new Array(100).fill(target);
//报错:类型‘number’的参数不能赋值给类型‘string’的参数
getStrArr(123);
​
//泛型参数默认类型
type IGetRepeatArr<T = number> = (target: T) => T[];
const getRepeatArr: IGetRepeatArr = target => new Array(100).fill(target);
//报错:类型‘string’的参数不能复制给‘number’的参数
getRepeatArr('123');

8.类型别名 & 类型断言

//提供type关键字定义了IObjArr的别名类型
type IObjArr = Array<{
    key: string;
    [objKey: string]: any;
}>
function keyBy<T extends IObjArr>(objArr: Array<T>) {
    //未指定类型时, result类型未 {}
    const result = objArr.reduce((res,val,key) => {
        res[key] = val;
        return res;
    },{});
    //通过as关键字,断言result类型为正确类型
    return result as Record<string,T>;
}

9.字符串/数字 字面量

//允许指定字符串/数字必须的固定值
​
//IDomTag必须为html,body,div,span中的其一
type IDomTag = 'html' | 'body' | 'div' | 'span';
//TDomTag必须为1,3,5,7,9中的其一
type IOddNumber = 1 | 3 | 5 | 7 | 9;

三.高级类型

1.联合/交叉类型

例:为设计列表编写类型

const bookList = [{
    author:'xiaoming',
    type:'history',
    range:'2001-2021',
},{
    author:'xiaoli',
    type:'story',
    theme:'love',
}]

类型声明繁琐,存在较多重复

interface IHistoryBook {
    author: string;
    type: string;
    range: string;
}
interface IStoryBook {
    author: string;
    type: string;
    theme: string;
}
type IBookList = Array<IHistoryBook | IStoryBook>;

解决方式:

  • 联合类型:IA | IB;联合类型表示一个值可以是几种类型之一
  • 交叉类型:IA & IB;多种类型叠加到一起成为一种类型,它包含了所需的所有类型的特性
type IBookList = Array<{
    author: string;
} & ({
    type:'history';
    range: string;
} | {
    type:'story';
    theme: string;
})>

2.类型保护与类型守卫

interface IA { a: 1, a1: 2}
interface IB { b: 1, b1: 2}
​
function log(arg: IA | IB) {
    //报错:类型“IA | IB”上不存在属性“a”。类型“IB”上不存在属性“a”
    //结论:访问联合类型时,出于程序安全,仅能访问联合类型中的交集部分
    ifarg.a) {
        console.log(arg.a1)
    } else {
        console.log(arg.b1)
    }
}
​
//--------------------------------
//类型守卫:定义一个函数,它的返回值是一个类型谓词,生效范围为子作用域
function getIsIA(arg: IA | IB): arg is IA {
    return !!(arg as IA).a;
}
function log2(arg: IA | IB) {
    if(getIsIA(arg)) {
        console.log(arg.a1);
    } else {
        console.log(arg.b1);
    }
}
//实现函数reverse
//其可将数组或字符串进行反转
function reverse(target: string | Array<any>) {
    //typof类型保护
    if(typeof target === 'string') {
        return target.split('').reverse().join('');
    }
    //instance类型保护
    if(target instanceof Object) {
        return target.reverse();
    }
}
//实现函数lofBook类型
//函数接收书本类型,并logger出相关特征
function logBook(book: IBookItem) {
    //联合类型 + 类型保护 = 自动类型推断
    if (book.type === 'history') {
        console.log(book.range);
    } else {
        console.log(book.theme);
    }
}

3.高级类型

//实现merge函数类型
//要求sourceObj必须为targetObj的子集
function mergel(sourceObj, targetObj) {
    const result = {...sourceObj };
    for(let key in targetObj) {
        const itemVal = sourceObj[Key];
        itemVal && ( result[key] = itemVal);
    }
    return result;
}
function merge2(souceObj,targetObj ) {
    return { ...sourceObj, ...targetObj};
}
interface Isourceobj {
    x?: string
    y?: string;
interface ITargetobj {
    X: string;
    y: string
}
type IMerge = (sourceObj: IsourceObj, targetObj: ITargetObJ => ITargetobj;
​
//类型实现繁琐:若obj类型较为复杂,则声明source和target便需要大量重复2遍
//容易出错:若target增加/减少key,则需要source联动去除
interface IMerge {
    <T extends Record<string, any>> (sourceObj: Partial<T>, targetObj: T): T;
}
​
type IPartial<T extends Record<string, any>> = {
    [P in keyof T]?: T[P];
}
//索引类型:关键字[keyof] ,其相当于取值对象中的所有key组成的字符串字面量,如type IKeys = keyof{a: string; b: number }; // => type IKeys = "a" | "b"
//关键字[in] ,其相当于取值字符串字面量中的一一种可能,配合泛型P,即表示每个key
//关键字[?] ,通过设定对象可选选项,即可自动推导出子集类型

4.函数返回值类型

//实现函数delayCall的类型声明
// delayCall接受一个函数作为入参,其实现延迟1s运行函数
//其返回promise,结果为入参函数的返回结果
function delayCall (func) {
    return new Promise(resolve => {
        setTimeout(O => {
            const result = func() ;
            resolve(result) ;
        },1000);
    });
}
type IDelaycall = <T extends 0 => any>(func: T) => ReturnType<T>;
type IReturnType<T extends (...args: any) => any> = T extends (...args: any => infer R ? R : any
//关键字[extends]跟随泛型出现时,表示类型推断,其表达可类比三元表达式
//如T ===判断类型?类型A :类型B
//关键字[infer] 出现在类型推荐中,表示定义类型变量,可以用于指代类型
//如该场景下,将函数的返回值类型作为变量,使用新泛型R表示,使用在类型推荐命中的结果中

四.工程应用

1.Web

image-20220807234407056.png

awesome-typescript-loader - npm (npmjs.com)

babel-loader - npm (npmjs.com)

2.Node

image-20220807234429287.png