TypeScript 基础

39 阅读11分钟

Ts支持所有的js代码,并且为js提供了类型提示,使得在编写js代码时就可以获得良好的代码提示,使得js代码更加的安全。提升了可读性和可维护性。最终会由编译器编译为Js代码供浏览器使用。

  1. 适用于周期较长的大型项目 适合团队开发 编写通用的代码库
  2. 试用 :www.typescriptlang.org/zh/play
  3. 文档 :www.tslang.cn/docs/handbo…
  1. TS 相对于 Js 进行了类型限制,使得对某种类型的数据只允许做该类型允许的操作,实现了类型安全
  2. 在运行时做类型检查叫做动态类型检查,写代码时比较灵活,但是只会在运行时报错,例如:JavaScript
  3. 如果在代码编译时做类型检查,叫做 静态类型检查,写代码时需要对变量的声明和相应操作,但是会在编译时报错

优势:(1)提供静态类型检查,在写代码阶段就可发现错误。

(2)良好的代码提示,更容易的写代码,减少bug。

安装npm install -g typescript

核心: 定义 任何东西都要 注明类型 调用 任何东西的时候都要 检查类型


ts环境

yarn add global typescript 全局安装ts

tsc -v 查看版本 tsc 文件名 编译文件(最终会编译成js)

在开发环境下,目前的脚手架程序基本上都可以很容易的创建ts环境

当在 TypeScript 项目中使用第三方库时,我们需要引用它的声明文件,才能获得对应的代码补全、接口提示等功能。

针对多数第三方库,社区已经帮我们定义好了它们的声明文件,我们可以直接下载下来使用。一般推荐使用 @types 统一管理第三方库的声明文件,@types 的使用方式很简单,直接用 npm 或 yarn 安装对应的声明模块即可。

一、类型注解

  • 语法:给变量添加类型 在变量后面用 : 来声明类型

使用 type来声明类型,一般首字母需要大写(类型别名)

在变量后可以使用 ? 表示可选,非必传

返回值类型声明为 void 的函数可以 没有返回值

可以使用as 来进行类型断言(as 可以理解为强制类型转换,来告诉ts 一个指定的类型,但是只在编译阶段生效)

二、类型简介

1. 简单类型

TypeScript支持所有js基础类型 number string boolean symbol undefinde null (小写)

********同时支持JavaScript的所有复合类型 Array class

let count:string = '100';

2. 复杂类型

(1)数组

使用 类型[] 或者 泛型 的方式来定义 特点是内部值固定为一种类型,如果需要多种类型请使用元组,

还可以使用 number[][]来定义二维数组。三维数组同理。

type A = Array<string>
let userList : A = ['one','two'] // 泛型写法
let userList:string[] = ['one','two']; //string[] 表示字符串数组
let userList:Array<string> = ['one','two']; // Array<elemType>泛型 规定数组内部值的类型
(2)元组

可以让同一个数组中同时存在多种类型,特点是适合指定长度的数组,因为要指定数组中每一项的类型

内部元素的个数和类型都固定的数组

let info:[number,string]; // 指定数组中每一项的类型,赋值必须严格执行
info=[100,'name'];// 数组中的类型和顺序都有要求
(3)函数

函数的类型声明主要针对参数和返回值,一旦声明完毕,参数和返回值的类型必须按照定义来传递。

type 声明的函数参数类型,在调用的时候参数可以少

函数参数如果有默认值,会被ts理解为可选参数

函数的可选参数也有可能会有类型保护的问题:如果用户不传值,就有可能是undefined,无法使用其属性

解决方法:加入if判断是否传值,只有为真时,才取值

// 指定参数的类型和声明变量十分相似
function Add(a:number,b:number):number{
  return a+b;
}

//括号内部为参数类型,外部为返回值类型
type x = (a:number,b:number)=> number // type关键字相当于为后面的类型声明起别名,void可以表示无返回值

const Add:x = (a,b)=>{
	return a + b;
}

// ? 表示可选参数,可选参数必须写到必填参数后面
function add (a:number,b?:string)
(4)interface 接口声明对象

interface关键字,声明一个可以继承 ****的对象(object,Function,构造器)类型,

注意:首字母大写,有些习惯接口名称首字母加I前缀,变量的值应该和接口规定保持一致

- readonly ****关键字可以在 interface 中声明一个只读属性,不可以二次赋值

- ? ****表示可选属性

- 其他 interface 声明的类型可以使用 ****extends继承 ,变量需要满足子接口和父接口声明的类型

- 类型可以嵌套声明

interface  Idemo{
  redonly name:string;  //只读
  herght?:number// 可选属性
}
interface Ison extends Idemo{
  height:number  //继承Idemo的属性
}

interface NumberArray {
  [index:number]:number; //如果索引是number,值也为number 定义数组类型,不常用
  [index:string]:any; // 定义任意类型
}

interface func{
  (a:string,b:string):string; //接口定义函数
}

interface sayHello{
  (name:string):string;
}

interface PersonConstructor {
    new (name: string, age: number): IPerson;
}

注意:TypeScript 支持直接使用字面量作为类型,例如:123,'字符串',{obj : ' 对象 '}

(5)Class
interface Options {
  name:string;
}
interface animal {  // 定义类
   option:Option,   // 定义类的参数
   eat():void  //定义类的内部方法
}
class  Dog implements animal {
  let option:Opiton 
  constructor(options:Opitons){
    this.optionS = options
  }
  eat():vodi{
    consosole.log(
      cosnole.log(this.options,'吃了骨头')
    )
  }
}
new Dog ({
  name:'旺财"
})

修饰class:

  • readonly 只读 , 修饰属性或索引签名,不可修改。
  • private 修饰方法,只能在内部使用,子类也不可以调用。
  • protected ****修饰方法,给继承的子类和父类自身内部使用
  • public ****默认为 public 可以在任意地方使用
(6)object

可以使用object声明 引用类型,包括 object , Array , Function ,一般用来做类型约束

let a : object = []  // 可以用来声明 Array 等引用类型
let b : {}  // 相当月 new Object , 可以声明所有类型 , 一般不用

3. TS 特殊类型(js中没有的)

  • void 含义:空类型,可以使用 as 断言指定类型 只能将 undefinde 和 null 赋值给 void
  • unknown 含义:未知类型,在使用时可以使用 as 断言指定类型,任何类型都可以赋值给unknown

但是unknow不可以赋值给其他类型。(可以理解为unknow是所有类型的联合)

  • never 含义:永远不存在的值,一般不用于声明,在类型判断时使用,或者函数抛出异常的时候,返回值为never。

never 可以赋值给任意类型,但是所有类型都不可以给never 赋值

  • any 含义:任意类型,可以赋值给任何类型(never除外),也可以接受任何类型的赋值

使用时候可以使用 as 断言指定类型

  • Enum 含义:枚举是一系列值的复合,enum的值只接受字符串string 和 数字number,可以用来做权限管理等等。
enum A {
  name:'小明',//name 枚举值,‘小明’ 真实值
  age:'18',
}

const example = A.age;

4. type and interface 对比

  • type “别名 ” 可以声明任何类型(基本类型和Object),
  • interface “接口 ” 只能声明对象(Array,Function,object,Date,Map,正则等),interface是类型声明
  • type声明后的值不可以重新赋值,扩展性较差。interface可以扩展。
  • interface 对类型中属性冲突的地方会直接报错,type 会转为never 不能使用
  • 注意:对外Api尽量使用interface方便扩展,对内api使用type ,防止代码分散。
  • 对于定义的属性,可以使用 来表示为可选属性, 可以使用 readonly 来指定为只读属性,通过只读属性构造的变量,其属性不可以改变。
interface  A {
  [k:string]:number
  // 声明索引类型和值类型,index是随机的,但是一般是采用含义相近的,例如 i,k,key等
}
interface A2 extends Array<string>{
  name:string,
}
interface A3 {
  (a:number,b:number):number
}
type A1 = Array<string> & {
  name:string
}
interface Itype {
  name? : string, // 可选属性
  readonly age : number, // 只读属性
}
// 1. 声明两个同名的interface,ts会自动将其属性合并,X会同时拥有name和age
interface X {
  name:string
}
interface x {
  age:number
}
const a:x={
  name:'小明',
  age:17
}

// 扩展全局的interface String 
declare global {
  interface String{
    foo(x:string):void
  }
}

三、高级类型

1. 联合类型

使用 | ****来声明联合类型,表示值为多种类型中的一种,typescript类型可以根据赋值来类型推断。

联合类型的类型保护:由于值可能是多种类型中的一个,所以只能使用所有类型的共有属性

为了避免这种情况 可以通过 in 或者 as 断言 来缩小联合类型范围,使代码更加安全

let a: number | string ;  // 表示 a 的类型是 number 和 string 中的一种

2. 交叉类型

使用 & ****来声明联合类型,表示值需要同时满足几种类型,可以配合interface等使用

let a: number & string ;  // 表示 a 的类型是 number 和 string 中的一种

3. 映射类型

type Demo<T> = {
  [Key in keyof T]?: T[Key]
}

//实现 pick<T,K>,从类型T中选出类型K,构造成一个新的类型
type R = MyPick<{a:1,b:2,c:3},"a">; // 从第一个对象中,找出key为a的类型

type MyPick<T,K extends keyof T>={
  [p in k]:T[p]
}

4. 泛型

  • 泛型是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性
  • 为了类型复用,泛型即为通用的类型,ts的类型体操就是对泛型进行逻辑处理。
  • <>内部的内容表示参数的类型,function foo<a:string参数类型>(a):string 返回值类型 {}
function Add<T>(a:T,b:T):T{
  return a+b;
}  // 使用t来表示类型待定,等传值时候再指定类型

let c = Add<number>(2,5);// 使用时传值


type a = string[];
//等价于
type a = Array<string>

四 类型运算

类型运算就是对传入的类型参数进行运算后返回新的类型。

1. 条件判断

extends ? : ts 类型的条件判断的语法是 条件 ? 分支1 : 分支2

type value = T extends U ? true : false // 如果T是U的子类,则返回true,反之为false

2. 推导

infer 提取类型的一部分,通过模式匹配的方式。

type Str<T extends string> = T extends `${infer Key}=${infer Value}`
  ? {
      [k in Key]: Value
    }
  : never

3. 利用js方法进行类型收窄

  • typeof 语法 typeof 变量名 返回值为类型的字符串格式,例如'number'

缺点:返回值仅支持常用类型,例如array,function都会返回 'object'

const f1 = (a:number | string)=>{
  if(typeof a==='number'){
    ....
  }else{
    ....
  }
}
  • instanceof 语法:a instanceof Date ****如果a 是日期的实例就返回true

缺点:不支持简单类型(instanceof基于原型),仅支持判断Object,any,或者泛型,不支持ts的独有类型

const f1 = (a:Date|Date[]){
  if (a instanceof Date){
    ...
  }else{
    ...
  }
}
  • in 判断
const f1 = (a : Person | Animal)=>{
  if ('name' in  a){
    ...
  }
}
  • Array.isArray() 判断
const f1 = (a:Array|function){
  if(Array.isArray(a)){
    ...
  }else{
    ...
  }
}

4. 利用 ts方法进行类型收窄

  • 可辨别联合类型

定义:ts 允许用户给多个类型添加同一字段从而判断联合类型的实际类型

这个字段的类型只能为基本类型,多个类型必须有同一个字段,不同类型的同名字段的类型尽量不要有交集

type A = {kind:'string',value:'number'}
type B = {kind:'number',value:'string'}

const f1 =(a:A|B){
  if(a.kind ==='string'){
    A类型
  }else{
    B类型
  }
}
  • is 类型谓词
type A = {
  name : string
  age : number
}
type B ={
  gender: string
  job : string
}
const f1 (example : A | B){
  // 利用函数判断example是否是A类型
  if(isA(example)){
    
  }
}

// 声明一个专门用来判断A类型或者B类型的函数,返回值为boolean
  function isA ( x : A | B ) : x is A{
      return 'name' in x && 'age' in x 
  }
  • as断言

直接使用as强制指定类型,进行 类型收窄。

5、 类型遍历 in keyof

  1. 索引名称 in keyof 类型 索引查询
type Person = {
  name: string
  age: number
}

type KeyofPersion = keyof Person // 'name' | 'age'

k in keyof Person
  1. T[Key] 是取索引类型某个索引的值,叫做索引访问。类似于js对象属性的访问方式,T 并没有实际意义,需要根据传递的值判断。
  2. in 是用于遍历联合类型的运算符。类似于 js 。

6、映射类型详解

  1. 映射值
type MapType<T> = {
    [Key in keyof T]: [T[Key], T[Key], T[Key]]
  // [Key in keyof T] 取出 key 的名字  即 a 和 b
  // T[Key]  取出 key 对应的值  即 1 和 2
}

type res = MapType<{a: 1, b: 2}>;
  1. 映射类型

因为索引类型(对象、class 等)可以用 string、number 和 symbol 作为 key,这里 keyof T 取出的索引就是 string | number | symbol 的联合类型,和 string 取交叉部分就只剩下 string 了。就像前面所说,交叉类型会把同一类型做合并,不同类型舍弃。

type MapType<T> = {
    [
        Key in keyof T 
            as `${Key & string}${Key & string}${Key & string}`
    ]: [T[Key], T[Key], T[Key]]
}
// Key in keyof T  取出 key 的名字
// as `${Key & string}${Key & string}${Key & string}` 将key 的名字 转化为 重复三次的字符串

类型工具

一、Record