Typescript 入门 | 青训营笔记

95 阅读6分钟

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

什么是Typescript

TypeScript 起源于使用JavaScript开发的大型项目 。由于JavaScript语言本身的局限性,难以胜任和维护大型项目开发。因此微软开发了TypeScript ,使得其能够胜任开发大型项目。

typescript发展历史

  • 2012-10:微软发布了TypeScript第一个版本(0.8)
  • 2014-10:Angular发布了基于TypeScript的2.0版本
  • 2015-04:微软发布了Visual Studio Code
  • 2016-05: @types/react发布,TypeScript可开发React
  • 2020-09: Vue 发布了3.0版本,官方支持 TypeScrip
  • 2021-11:v4.5版本发布

为什么使用typescript

JavaScript:动态类型,弱类型语言

typescript:静态类型,弱类型语言

静态类型

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

JS的超集

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

基本语法

基本数据类型

JavaScript

/* 字符串 */
const q = 'string';

/* 数字 */
const w = 1;

/* 布尔值 */
const e = true;

/* null */
const r = null;

/* undefined */
const t = undefined;

Typescript

/* 字符串 */
const q: string = 'string';

/* 数字 */
const w: number = 1;

/* 布尔值 */
const e: boolean = true;

/* null */
const r: null = null;

/* undefined */
const t: undefined = undefined;

对象类型

const bytedance: IBytedancer = {
    jobId: 9303245,
    name: 'Zheng',
    sex: 'man',
    age: 28,
    hobby: 'swimming',
}

interface IBytedancer {
    // 只读属性:约束属性不可在对象初始化外赋值
    readonly jobId: number;
    name: String;
    sex: 'man' | 'woman' | 'other';
    age: number;
    // 可选属性:定义该属性可以不存在
    hobby?: string;
    // 任意属性:约束所有对象属性都必须是该属性的子类型
    [key: string]: any;
}

// 报错:无法分配到"jobId",因为它是只读属性
bytedance.jobId = 123;
// 成功:任意属性标注下可以添加任意属性
bytedance.plateform = 'data';
// 报错:缺少属性"name",hobby可缺省
const bytedance2: IBytedancer = {
    jobId: 456,
    sex: 'woman',
    age: 18
}

函数类型

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;

函数重载

// 对getDate函数进行重载,timestamp为可缺省参数
function getDate(type: 'string', timestamp?: string): string;
function getDate(type: 'date', timestamp?: string): Date;
function getDate(type: 'string' | 'date', timestamp?: string): Date | string{
    const date = new Date(timestamp);
    return type == 'string' ? date.toDateString() : 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;
function getDate(type: 'date', timestamp?: string): Date;
function getDate(type: 'string' | 'date', timestamp?: string): Date | string{
    const date = new Date(timestamp);
    return type == 'string' ? date.toDateString() : date;
}

const x = getDate('date'); // x: Date
const y = getDate('string', '2018-01-10'); // y: string

// 可简化为
interface IGetDate{
    (type: 'string', timestamp?: string): string;
    (type: 'date', timestamp?: string): Date;
    (type: 'string' | 'date', timestamp?: string): Date | string;
}

// 报错:不能将类型“(type: any, timestamp: any) => string | Date”分配给类型“IGetDate”。不能将类型“string | Date”分配给类型“string”。不能将类型“Date”分配给类型“string”。ts(2322)
const getDate2: IGetDate = (type,timestamp) => {
    const date = new Date(timestamp);
    return type == 'string' ? date.toDateString() : date;
}

数组类型

// 类型+方括号 表示
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, {}, []];

数组类型

// 空类型,表示无赋值
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 INumArr = Array<number>;

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 IMane<T>{
    instance: T;
}
// 泛型别名
type ITypeArr<T>= Array<T>;

// 泛型约束:限制泛型必须符合字符串
type IGetRepeatStringArr = <T extends string>(target: T) => T[];
const getStrArr: 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');

类型别名&类型断言

// 通过type关键字定义了I0bjArr的别名类型
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>;
}

字符串/数字字面量

// 允许指定字符串/数字必须的固定值

// IDomTag必须为html、body、div、span中的其一
type IDomTag = 'html' | 'body' | 'div' | 'span';
// IOddNumber必须为1、3、5、7、9中的其一
type IOddNumber = 1 | 3 | 5 | 7 | 9;

高级类型

联合/交叉类型

// 为书籍列表编写类型
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',
    range: string,
})>

类型保护与类型守卫

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

function log(arg: IA | IB) {
    // 报错:类型“IA| IB"上不存在属性"a”。类型“IB"上不存在属性“a”
    // 结论:访问联合类型时,处于程序安全,仅能访问联合类型中的交集部分
    if (arg.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();
    }
}

工程应用

Web

webpack

  1. 配置webapack loader相关配置
  2. 配置tsconfig.Jjs文件
  3. 运行webpack启动/打包
  4. loader处理ts文件时,会进行编译与类型检查

Node

使用TSC编译

  1. 安装Node与npm
  2. 配置tsconfig.js文件
  3. 使用npm安装tsc
  4. 使用tsc运行编译得到js文件