TypeScript入门 | 青训营笔记

52 阅读4分钟

TypeScript入门 | 青训营笔记

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

为什么选择TS

TS与JS最大的区别就在于TS是静态类型的语言,而JS是动态类型的语言。

在TS中,由于变量定义了类型,这样再多人开发时可维护性大大增强,在编译时期就可以暴露出错误信息,提高开发效率。

TS 包含兼容所有JS的特性,支持调用JS

基本语法

类型

在TS中,变量的声明必须要有类型,否则编译器会报错。

let a: number;

TS支持以下基本类型声明

  • number
  • string
  • boolean
  • any
  • void
  • null
  • undefined
  • never
  • symbol
let a: number = 1;
let b: string = '1';
let c: boolean = true;
let d: any = 1;
let e: void = undefined;
let f: null = null;
let g: undefined = undefined;
let h: never = (() => { throw new Error('error') })();
let i: symbol = Symbol('symbol');

以及可以声明为对象

interface Person {
    readonly id: number;
    name: string;
    age: number;
    // 构造时可以不存在
    money?: number;
    // 任意属性,约束所有对象属性都必须是该属性的子类型
    [key: string]: any;
}

let person: Person = {
    id: 1,
    name: '张三',
    age: 18
}
// 会报错 因为定义为readonly只读
person.id = 2;

声明为函数

function add(x: number, y: number): number {
    return x + y;
}

// 拆开为const mult: (x: number, y: number) => number和(x, y) => x * y理解
const mult: (x: number, y: number) => number = (x, y) => x * y;

// 也可以通过
interface IMult {
    (x: number, y: number): number;
}
const mult2: IMult = (x, y) => x * y;

重载

我们可以通过同名函数而参数不同来实现重载

function getDate(type: 'string', timestamp?: string): string;
function getDate(type: 'date', timestamp?: string): Date;
function getDate(type: 'string' | 'date', timestamp?: string): string | Date {
    if (type === 'string') {
        return timestamp;
    } else {
        return new Date(timestamp);
    }
}

getDate('date'); // Date
getDate('string', '2018-01-10'); // string

问题代码

interface IGetDate {
    (type: 'string', timestamp?: string): string;
    (type: 'date', timestamp?: string): Date;
    (type: 'string' | 'date', timestamp?: string): string | Date;
}

const getDate2: IGetDate = (type, timestamp) => {
    const date = new Date(timestamp);
    return type === 'string' ? date.toLocaleString() : date;
}

以上代码会报错,因为我们定义了一个匿名函数,担赋值给了一个IGetDate变量,产生了错误 不能将类型"(type: any,timestamp: any) => string | Date"分配给类型"IGetDate"。不能将类型"string | Date"分配给类型“string”。 不能将类型“Date"分配给类型“string"。ts (2322)

数组

通过类型+[]可以声明数组

/*「类型+方括号」表示*/
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,2345,6];
const arr2: IArr2 = [12'3','4', { a: 1 }];const arr3: IArr3 = [12'3','4'];
const arr4: IArr4 = [ 'string' , ()=>null,,[]];

补充类型

  • 空类型: () => void
  • 任意类型: any
  • 枚举类型: enum
  • 泛型: type InumArr = Array<number>

泛型

通过泛型我们可以把类型放在尖括号中,这样我们就可以在使用时再指定类型。

function getRepeatArr(target) {
    return Array(100).fill(target);
}

type IGetRepeatArr = (target: any) => any[];

type IGetRepeatArrR = <T>(target: T) => T[];

// 泛型接口 & 多泛型
interface IX<T, U> {
    key: T;
    value: U;
}

// 泛型类
class X<T, U> {
    key: T;
    value: U;
}

// 泛型别名
type ITypeArr = Array<T>;

// 泛型约束 使用时只能使用number类型或者string类型
type ITypeArr2 = Array<T extends number | string>;

// 通过as关键字,实现类型断言
function test<T extends ITypeArr>(objArr: Array<T>) {
    const result = objArr.reduce((pre, cur) => {
        return pre + cur;
    } , {});
    return result as Record<string, T>;
}

字面量

通过 | 来限制必须使用的固定值

// 只能使用下面枚举的值
type IType = 'string' | 'number';
type IOdd = 1 | 3 | 5 | 7 | 9;

高级类型

联合/交叉类型

联合类型

可以通过 | 标识一个值可以是该组合中的一种


// 我们可以选择其中一个进行定义
type IBookList = Array<{
    {
        type: 'history';
        range: string;
    } | {
        type: 'science';
        theme: string;
    }
}

交叉类型

可以通过 & 标识一个值需包含该组合中的所有类型


type IBookList = Array<{
    type: 'history';
    range: string;
} & {
    class: string;
    type: 'science';
}>

类型保护

通过类型保护,我们可以接收多个类型,但只能访问它们的交集

interface IA {a: 1, a1: 2, c}
interface IB {b: 1, b1: 2, c}
function log(arg: IA | IB) {
    // 错误
    console.log(arg.a);
    // 正确
    console.log(arg.c);
}

当访问联合类型时,我们只能访问所有联合类型的交集

类型守卫

通过类型守卫,我们可以指定一个类型的可能性,只有当类型守卫的类型与实际类型相同时,才能访问该属性或方法

interface IA {a: 1, a1: 2, c}
interface IB {b: 1, b1: 2, c}

function getIsIA(arg: IA | IB): arg is IA {
    // 当类型守卫的类型与实际类型相同时,才能访问该属性或方法
    // !!将类型转换为布尔值再翻转
    return !!(arg as IA).a;
}

function getIsIB(arg: IA | IB): arg is IB {
    return !!(arg as IB).b;
}

function log(arg: IA | IB) {
    if (getIsIA(arg)) {
        console.log(arg.a);
    } else if (getIsIB(arg)) {
        console.log(arg.b);
    }
}

实例

function reverse(target: string | Array<any>) {
    // type类型保护
    if (typeof target === 'string') {
        return target.split('').reverse().join('');
    }
    // instanceof类型保护
    if (target instanceof Array) {
        return target.reverse();
    }
}


function logBook(book: IBookList) {
    // 访问交集,类型保护
    if (book.type === 'history') {
        console.log(book.range);
    } else if (book.type === 'science') {
        console.log(book.theme);
    }
}

工程应用

由于ts并不被原生的js引擎所支持,所以我们需要使用第三方的引擎来支持ts,例如通过webpack进行转换,转为js后再运行

使用TSC进行编译的过程

graph LR
    A[Code.ts]--->B[webpack]--->C[TSC]--->D[Code.js]

webpack及vite相关内容代后续笔记完成后补充到这里