TypeScript 已经逐渐成为前端必备技能了,随着 TS 4.0 和 Vue 3 的发布,TS 准备迎接大流行的趋势了,这篇文章,让你对 TS 有基本的认识和入门。
TypeScript 介绍 什么是 TypeScript 是 JavaScript 的一个超集,它可以编译成纯 JavaScript。编译出来的 JavaScript 可以运行在任何浏览器上,主要提供了类型系统 和对 ES6 的支持 。
为什么选择 TypeScript 增加了代码的可维护性 包容性强,支持 ES6 语法,.js文件直接重命名为.ts即可 兼容第三方库,即使第三方库不是 TypeScript 写的,也可以通过单独编写类型文件供识别读取 社区活跃,目前三大框架还有越来越多的库都支持 TypeScript 了 TypeScript 只会在编译的时候对类型进行静态检查,如果发现有错误,编译的时候就会报错。
安装 TypeScript 全局安装 TS 查看 TypeScript 版本号 我当前版本为 Version 4.0.2
初始化生成 tsconfig.json 文件 在 tsconfig.json 中设置源代码目录和编译生成 js 文件的目录 1 2 "outDir": "./dist", "rootDir": "./src" 
监听 ts 文件的变化,每当文件发生改变就自动编译 之后你写的 ts 文件编译错误都会直接提示,如果想运行文件,就到 /dist目录下找相应的 js 文件,使用 node 运行即可
ts-node 安装 当然这样其实也挺麻烦,我们想直接运行 TS 文件, 这时可以借助ts-node插件
全局安装
找到文件路径,运行即可
基础类型 Number 类型 Boolean 类型 1 let  isShow: boolean  = true 
String 类型 1 let  str: string  = 'hello' 
Array 类型 1 2 let  arr1: number [] = [1 , 2 , 3 ]let  arr2: Array <number > = [2 , 3 , 4 ]
Any 类型 1 2 3 let  foo: any  = 'hello' foo = 12  foo = false  
Null 和 Undefined 类型 null 和 undefined 可以赋值给任意类型的变量
1 2 3 4 5 6 let  test1: undefined  = undefined let  test2: null  = null let  test3: number let  test4: string test3 = null  test4 = undefined  
Void 类型 void 类型像是与 any 类型相反,它表示没有任何类型。当一个函数没有返回值时,其返回值类型是 void
1 2 let  test5: void  = undefined  function  testFunc ( ): void   {} 
Never 类型 never 类型表示的是那些永不存在的值的类型。
1 2 3 function  bar ( ): never   {  throw  new  Error ('never reach' ) } 
Unknown 类型 所有类型都可以赋值给 any,所有类型也都可以赋值给 unknown。
1 2 3 4 5 6 7 8 9 10 11 12 13 let  value: unknownvalue = 123  value = 'Hello'  value = true  let  value1: unknown = valuelet  value2: any  = valuelet  value3: boolean  = value let  value4: number  = value let  value5: string  = value let  value6: object = value let  value7: any [] = value let  value8: Function  = value 
unknown 类型只能被赋值给 any 类型和 unknown 类型本身。
Tuple 类型 数组合并了相同类型的对象,而元组(Tuple)合并了不同类型的对象。元组表示一个数量和类型都已知的数组
1 2 let  tupleArr1: [string , number ] = ['hello' , 10 ]
Enum 类型 使用枚举可以定义一些带名字的常量。 TypeScript 支持数字的和基于字符串的枚举。
1 2 3 4 5 6 7 8 9 10 11 12   enum  Season {     Spring,     Summer,     Autumn,     Winter,   }   let  a: Season = Season.Spring   let  b: Season = Season.Summer   let  c: Season = Season.Autumn   let  d: Season = Season.Winter   console .log(a, b, c, d)  } 
函数类型 函数声明 1 2 3 4 5 6 7 8 function  func1 (x, y )  {  return  x + y } function  func2 (x: number , y: number  ): number   {  return  x + y } 
函数表达式 在 TypeScript 的类型定义中,=> 用来表示函数的定义,左边是输入类型,需要用括号括起来,右边是输出类型。
1 2 3 4 5 6 7 8 9 10 11 12 13 let  func3 = function  (x, y )  {  return  x + y } let  func4 = function  (x: number , y: number  ): number   {  return  x + y } let  func5: (x: number , y: number  ) =>  number  = function  (x: number , y: number  ): number   {  return  x + y } 
接口定义函数的形状 采用函数表达式|接口定义函数的方式时,对等号左侧进行类型限制,可以保证以后对函数名赋值时保证参数个数、参数类型、返回值类型不变。
1 2 3 4 5 6 7 8 interface  SearchFunc {  (source: string , subString: string ): boolean  } let  mySearch: SearchFuncmySearch = function  (source: string , subString: string  )  {   return  source.search(subString) !== -1  } 
可选参数 对于 TS 的函数,输入多余的(或者少于要求的)参数,是不允许的,那么该怎么设置可选参数呢?,我们可以在声明之后加一个问号
1 2 3 4 5 6 7 8 9 10 11 function  getName1 (firstName: string , lastName?: string  )  {     if  (lastName) {     return  `${firstName}  ${lastName} `    } else  {     return  firstName   } } let  name1 = getName1('jacky' )let  name2 = getName1('jacky' , 'lin' )console .log(name1, name2)
参数默认值 1 2 3 4 5 function  getName2 (firstName: string  = 'monkey', lastName: string  )  {  return  `${firstName}  ${lastName} `  } console .log(getName2('jacky' , 'Lin' ))console .log(getName2(undefined , 'Lin' )) 
void 和 never 类型 当函数没有返回值时,可以用 void 来表示。
当一个函数永远不会返回时,我们可以声明返回值类型为 never
1 2 3 4 5 6 7 function  func6 ( ): void   {   } function  func7 ( ): never   {  throw  new  Error ('never reach' ) } 
剩余参数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 function  push1 (arr, ...items )  {  items.forEach(function  (item )  {     arr.push(item)   }) } let  a: any [] = []push1(a, 1 , 2 , 3 ) function  push2 (arr: any [], ...items: any [] ): void   {  items.forEach(function  (item )  {     arr.push(item)   }) } let  b: any [] = []push1(b, 1 , 2 , 3 , '5' ) 
函数参数为对象(解构)时 1 2 3 4 5 6 7 8 9 10 11 12 13 function  add ({ one, two } )  {  return  one + two } const  total = add({ one: 1 , two: 2  })function  add1 ({ one, two }: { one: number ; two: number  } ): number   {  return  one + two } const  three = add1({ one: 1 , two: 2  })
函数重载 重载允许一个函数接受不同数量或类型的参数时,作出不同的处理。
1 2 3 4 5 6 7 8 9 10 function  reverse (x: number  ): number  // 函数定义function  reverse (x: string  ): string  // 函数定义function  reverse (x: number  | string  ): number  | string   {  // 函数实现   if  (typeof  x === 'number ' )  {    return  Number (x.toString().split('' ).reverse().join('' ) )   } else  if  (typeof  x === 'string ' )  {    return  x.split('' ).reverse().join('' )   } } 
类型断言 TypeScript 允许你覆盖它的推断,并且能以你任何你想要的方式分析它,这种机制被称为「类型断言」。
TypeScript 类型断言用来告诉编译器你比它更了解这个类型,你知道你自己在干什么,并且它不应该再发出错误。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 interface  Cat {  name: string    run(): void  } interface  Fish {  name: string    swim(): void  } function  getName (animal: Cat | Fish ): string   {  return  animal.name } function  isFish (animal: Cat | Fish ): boolean   {  if  (typeof  (animal as  Fish).swim === 'function' ) {     return  true    }   return  false  } ;(window  as  any ).randomFoo = 1  
1 2 3 4 5 6 7 let  value1: any  = 'hello' let  value1Length: number  = (<string >value1).lengthlet  value2: any  = 'world' let  value2Length: number  = (value2 as  string ).length
类 存取器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class  Animal {  constructor (name: string  ) {     this .name = name   }      get  name() {     return  '名字'    }      set  name(value: string ) {     console .log('setter: '  + value)   } } let  animal = new  Animal('monkey' )console .log(animal.name) animal.name = 'mk'   
访问修饰符 public 修饰的属性或方法是公有的,可以在任何地方被访问到,默认所有的属性和方法都是 public 的 private 修饰的属性或方法是私有的,不能在声明它的类的外部访问 protected 修饰的属性或方法是受保护的,它和 private 类似,区别是它在子类中也是允许被访问的 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class  Person {  public  name   private  age   protected  sex   public  constructor (name: string , age: number , sex: string  ) {     this .name = name     this .age = age     this .sex = sex   } } let  person1 = new  Person('jacky' , 22 , 'man' )person1.name = 'monkey'   class  Person1 extends  Person {  constructor (name: string , age: number , sex: string  ) {     super (name, age, sex)        } } 
参数属性 同时给类中定义属性的同时赋值
1 2 3 4 5 6 7 8 9 10 11 12 class  Animal3 {  public  name   constructor (name: string  ) {     this .name = name   } } console .log(new  Animal3('animal3' ).name) class  Animal2 {  constructor (public  name: string  ) {}  } console .log(new  Animal2('animal2' ).name) 
1 2 3 4 5 6 7 8 class  Animal4 {  readonly name   constructor (name: string  ) {     this .name = name   } } let  animal4 = new  Animal4('animal4' )
抽象类 1 2 3 4 5 abstract  class  CommonAnimal {  name: string    abstract  speak(): void  } 
static 1 2 3 4 5 6 class  Animal5 {  static  sayHi() {     console .log('Hello Animal5' )   } } Animal5.sayHi() 
联合类型 联合类型(Union Types)表示取值可以为多种类型中的一种。使用一个|分割符来分割多种类型
1 2 3 4 let  foo: string  | number  | boolean foo = 'test'  foo = 3  foo = true  
交叉类型 1 2 3 4 5 6 7 8 9 10 11 12 interface  IPerson {  id: string    age: number  } interface  IWorker {  companyId: string  } type  IStaff = IPerson & IWorkerconst  staff: IStaff = { id: '007' , age: 24 , companyId: '1'  }
类型别名 1 2 3 4 5 6 7 8 9 type  Message = string  | string []let  getMsg = (message: Message ) =>  {  return  message } type  Weather = 'SPRING'  | 'SUMMER'  | 'AUTUMN'  | 'WINTER' let  weather1: Weather = 'SPRING' let  weather2: Weather = 'AUTUMN' 
接口 对象的形状 1 2 3 4 5 6 7 8 interface  Person1 {  name: string    age: number  } let  person1: Person1 = {  name: 'jacky' ,   age: 23 , } 
描述行为的抽象 1 2 3 4 5 6 7 8 9 10 11 12 13 14 interface  AnimalLike {  eat(): void    move(): void  } interface  PersonLike extends  AnimalLike {  speak(): void  } class  Human implements  PersonLike {  speak() {}   eat() {}   move() {} } 
含构建函数作参数的写法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class  Animal1 {  constructor (public  name: string  ) {}   age: number  } class  Animal2 {  constructor (public  age: number  ) {} } interface  WithNameClass {  new  (name: string ): Animal1 } function  createClass (classname: WithNameClass, name: string  )  {  return  new  classname(name) } let  instance1 = createClass(Animal1, 'monkey' )
其它任意属性 1 2 3 4 interface  Person2 {  readonly id: number    [propName: string ]: any   } 
泛型 泛型是指定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性;通常用 T 表示,但不是必须使用改字母,只是常规,通常还有其他常用字母:
T(Type):表示一个 TypeScript 类型 K(Key):表示对象中的键类型 V(Value):表示对象中的值类型 E(Element):表示元素类型 泛型类 1 2 3 4 5 6 7 class  GenericNumber<T> {  name: T   add: (x: T, y: T ) =>  T } let  generic = new  GenericNumber<number >()generic.name = 123  
泛型数组 1 2 3 4 5 function  func <T >(params: T[] )  {  return  params } func<string >(['1' , '2' ]) func<number >([1 , 2 ]) 
1 2 3 4 5 function  func1 <T >(params: Array <T> )  {  return  params } func1<string >(['1' , '2' ]) func1<number >([1 , 2 ]) 
泛型接口 可以用来约束函数
1 2 3 4 interface  Cart<T> {  list: T[] } let  cart: Cart<number > = { list: [1 , 2 , 3 ] }
泛型别名 1 2 3 type  Cart2<T> = { list: T[] } | T[]let  c1: Cart2<number > = { list: [1 , 2 , 3 ] }let  c2: Cart2<number > = [1 , 2 , 3 ]
泛型接口 VS 泛型别名
接口创建了一个新的名字,它可以在其他任意地方被调用。而类型别名并不创建新的名字 类型别名不能被 extends 和 implements,这时我们应该尽量使用接口代替类型别名 当我们需要使用联合类型或者元组类型的时候,类型别名会更合适 多个泛型 1 2 3 4 5 6 7 8 function  swap <T , P >(tuple: [T, P] ): [P , T ]  {  return  [tuple[1 ], tuple[0 ]] } let  ret = swap([1 , 'a' ])ret[0 ].toLowerCase() ret[1 ].toFixed(2 ) 
默认泛型 1 2 3 4 5 6 7 8 function  createArray <T  = number >(length: number , value: T ): T []  {  let  arr: T[] = []   for  (let  i = 0 ; i < length; i++) {     arr[i] = value   }   return  arr } let  arr = createArray(3 , 9 )
泛型约束(继承) 1 2 3 4 5 6 7 8 9 10 interface  WithLength {  length: number  } function  logger <T  extends  WithLength >(val: T )  {  console .log(val.length) } logger('hello' ) logger([1 , 2 , 3 ]) 
泛型工具类型 为了方便开发者 TypeScript 内置了一些常用的工具类型,比如 Partial、Required、Readonly、Pick 等。它们都是使用 keyof 实现。
keyof 操作符可以用来一个对象中的所有 key 值。
1 2 3 4 5 6 7 8 9 interface  Person1 {  name: string    age: number    sex?: string  } type  PersonKey = keyof Person1 function  getValueByKey (p: Person1, key: PersonKey )  {  return  p[key] } 
内置的工具类型 1 2 3 4 5 6 7 8 9 10 11 type  PersonSearch = Partial<Person> type  PersonRequired = Required<Person> type  PersonReadOnly = Readonly<Person> type  PersonSub = Pick<Person, 'name' > 
类数组 1 2 3 let  root = document .getElementById('root' )let  children: HTMLCollection = root!.children let  childNodes: NodeListOf<ChildNode> = root!.childNodes
类型保护 更明确的判断某个分支作用域中的类型,主要尝试检测属性、方法或原型,以确定如何处理值。
typeof 1 2 3 4 5 6 7 8 9 10 function  double (input: string  | number  | boolean  ): number   {     if  (typeof  input === 'string' ) {     return  input.length   } else  if  (typeof  input === 'number' ) {     return  input   } else  {     return  0    } } 
instanceof 1 2 3 4 5 6 7 8 9 10 11 12 13 class  Monkey {  climb: string  } class  Person {  sports: string  } function  getAnimalName (animal: Monkey | Person )  {  if  (animal instanceof  Monkey) {     console .log(animal.climb)   } else  {     console .log(animal.sports)   } } 
in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class  Student {  name: string    play: string [] } class  Teacher {  name: string    teach: string  } type  SchoolRole = Student | Teacherfunction  getRoleInformation (role: SchoolRole )  {  if  ('play'  in  role) {     console .log(role.play)   }   if  ('teach'  in  role) {     console .log(role.teach)   } } 
自定义类型保护 比如有时两个类型有不同的取值,也没有其他可以区分的属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 interface  Bird {  leg: number  } interface  Dog {  leg: number  } function  isBird (x: Bird | Dog ): x  is  Bird   {  return  x.leg === 2  } function  getAnimal (x: Bird | Dog ): string   {  if  (isBird(x)) {     return  'bird'    } else  {     return  'dog'    } } 
上述完整代码示例:typescript-tutorial