Typescrtpt入门 | 青训营笔记

137 阅读7分钟

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

为什么、什么是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版本,官方支持 TypeScript
  • 2021-11: v4.5版本发布

js与ts对比:

JS:动态类型 弱类型语言

TS: 静态类型 弱类型语言

静态类型

  • 可读性增强:基于语法解析TSDoc,,ide增强
  • 可维护性增强:在编译阶段暴露大部分错误

=> 多人合作的大型项目中,获得更好的稳定性和开发效率

JS的超集

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

编辑器推荐

Visual Studio Code

TypeScript在线编辑器

基本语法

基础数据类型

//JS
/*字符串*/
const q = 'string';
/*数字*/
const w = 1;
/*布尔值*/
const e = true;
/*null */
const r = null;
/* undefined */
const t = undefined;

=>
//TS
/*字符串*/
const qi string = 'string ' ;
/*数字*/
onst w: number = 1;
/*布尔值*/
const e: boolean = true; 
/*null */
const r: null = null;
/*undefined */
const t: undefined = undefined;

对象类型

const bytedancer: IBytedancer = {
//大写的I开头用于表示一个类型
    jobId: 9303245,“
    name: 'Lin',
    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"",因为它是只读属性,定义完bydancer后对只读属性jobId赋值之后再对其赋值会报错*/
bytedancer.jobId = 12345;
/*成功:任意属性标注下可以添加任意属性*/
bytedancer.plateform = 'data';
/*报错:缺少属性“name"" , hobby可缺省*/
const bytedancer2: IBytedancer = {
    jobId: 89757,
    sex: 'woman ' ,
    age: 18,
}

函数类型

function add(x, y){
    return x +y;
}
const mult = (x,y)=>×*y;

function add(x: number, y: number): number {
//补充参数类型,返回值类型
    return x + y;
}
const mult: (x: number,y: number) =>number = (x, y)=>× * y;
//定义了一个函数类型

//支持interface定义函数
interface IMult {
    (x: number, y: number): number;
}
const mult: IMult =(×, 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.toLocaleString() : 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"。
不能将类型“stringDate”分配给类型“string"。
不能将类型"Date"分配给类型"string"。ts (2322)*/
const getDate2: IGetDate = (type,timestamp
) => {
    const date = new Date(timestamp);
    return type == 'string' ? date.toLocaleString() : 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 = [123456];
const arr2: IArr2 = [1,2'3','4', { a: 1 }];
const arr3: IArr3 = [1,2,'3''4'];
const arr4: IArr4=['string',()=>null,{},[]];

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 INumArr = Array<number>;

//泛型例子
function getRepeatArr(target){
    return new Array(100).fill(target);
}
type IGetRepeatArr = (target: any) =>any[];
/*出不预先指定具体的类型,而在使用的时候再指定类型的一种特性*/
type IGetRepeatArrR=<T>(target: T) T[];
//定义了一个t的变量,变量未知,在函数执行过程中确定

/*泛型接口&多泛型*/
interface IX<T,U> {
    key: T;
    val: U;
}
/*泛型类*/
class IMan<T> {
    instance: T;
}
/*泛型别名*/
type ITypeArr<T> = Array<T>; 


/*技型约束:限制(extends)泛型必须符合字符串*/
type IGetRepeatStringArr =<T extends string>(target: T) => T[];
const getStrArr: IGetRepeatStringArr = target => new Array(100).fill(target);
/*报错:类型"number"的参数不能赋给类型"string"的参数*/
getStrArr(123);

/泛型参数默认类型number*/
type IGetRepeatArr<T = number> = (target: T)=>T[];
const getRepeatArr: IGetRepeatArr = target => new Array(100).fill(target);
/*承报错:类型"string"的参数不能赋给类型"number"的参数*/
getRepeatArr( '123');

类型别名&类型断言

/*通过type关键字定义了IObjArr的别名类型
取了一个其他的名字承载复杂的变量*/
type IObjArr = Array<{
    key: string;
    [objKey: string]: any;//任意类型
}>//复用类型
//类型断言
function keyBy<T extends IObjArr>(objArr: Array<T>){
/*来未指定类型时,result类型为{}
将IobjArr转换为object*/
    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;

高级类型

联合/交叉类型

image.png

联合类型: IA | IB;联合类型表示一个值可以是几种类型之一 交叉类型: IA&IB;多种类型叠加到一起成为一种类型,它包含了所需的所有类型的特性

image.png

类型保护与类型守卫

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(ara: 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();
    }
}
//当两个类型完全没有任何重合点时才需要类型守卫

//实现函数logBook类型
//函数接受书本类型,并logger出相关特征
function logBook(book: IBookItem) {
//联合类型+类型保护=自动类型推断
    if (book.type === 'history ') {
        console.log(book.range);
    }else {
        console.log(book.theme);
    }
}

/*实现merge函数类型
要求sourceObj必须为targerobj的子集
*/
function merge1(sourceObj, targetObj) {
    const result = { ...sourceObj };
    for( let key in targetObj){
        const itemVal = sourceObj [key];
        itemVal &&( result[key]- itemVal );
    }
    return result;
}
unction merge2(source0bj, target0bj){
    return { ...sourceObj, -..targetObj) ;
}

interface ISource0bj {
    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>> = {
    [Pin keyof T]?: T[P];
}
//索引类型;关键字[keyof],其相当于取值对象中的所着key组成的字符串空面量,如
type IKeys= keyof { a: string; b: number }; // => type IKeys = "a" | b"
//关键字【in】,其相当于取值字符串字面量中的一种可能,配合泛型P,即表示每个key
//关键字【?】,通过设定对象可选选项,即可自动推导出子集类型

函数返回值类型

实现函数delayCall的类型声明

delayCallI接受一个函数作为入参:其实现延迟1s运行函数

其返回promise,结果为入参函数的返回结果

function delaycall(func){
    return new Promise( resolve=>{
        setTimeout(()=>{
            const result = func();
            resolve( result);
        },1000);
    });
}
type IDelayCall = <T extends () => 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表示,使用在类型推荐命中的结果中

工程应用

webpack

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

Node

使用TSC编译

image.png

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