TypeScript 教程

一级目录

二级目录

三级目录

npm i typescript -g

yarn tsc -v

运行

tsc hello.ts

# 自动监听
tsc hello.ts -w

# 运行js
node hello.js

基本数据类型

  • 基本类型、类型校验、类型推断:可选值?
  • union:联合声明 |
  • any:任意类型
  • unknown:未知类型和any一样不同点在于需要明确类型后赋值
  • void & never
    • 区别点在于 void 可用于返回值但返回值必须是 null 或 undefined,而 never 不存在返回值。
  • null & undefined
  • 函数
    • 函数的定义分为 函数声明 和 函数表达式 定义。
    • 函数定义 需要对输入输出进行约束。
    • 函数表达式中的 => 不同于ES6的箭头函数,=> 的左边是输入类型,右边是输出类型。
    • 重载、默认参数、可选参数?、剩余参数...rest

基本类型

  • JavaScript 的类型分为两种:原始数据类型(Primitive data types)和对象类型(Object types)。
  • 原始数据类型包括:Boolean、Number、String、Array、Tuple元组、Enum枚举、Unknown、Any、Void、Null 和 Undefined、Never。Object表示非原始类型
  • 数组和元组的区别在于,元组各元素的类型不必相同。
  • 使用 : 指定变量的类型,: 的前后有没有空格都可以。

当限定对象值类型时,可以声明为对象、数组、原型对象。

// 定义基本类型
const num:number = 100
const str:string = 'zyugat'
const boo:boolean = true
let obj:object = {'zyugat', 100}

// 限定对象值类型
let obj:{name: string,year:number}
obj = {} // 使用字面量声明对象
obj = [] // 数组是对象
obj = Object.prototype // 原型对象
obj='zyugat' // 报错,改变了类型为字符串

// 数组
let str:string[] =[]
let arr:Array<string> =[]
let arr = new Array<string>(3).fill('zyugat.com')

// 元组
let arr:[string, number, boolean]
arr = ['zyugat.com', 2090, true]

? :可选值

// 加 ? 代表非必选
let obj:{name: string,year:number,url?:string}

类型校验

编译时会报错,因为a不为数字

// 类型校验
function sum(a:number,b:number){
	return a+b;
}
console.log(sum('a',3)) 

类型推断

当没有明确设置类型时,系统会根据值推断变量的类型

// 字符串
let str = 'zyugat';	// let str: string
str = 18;	// 报错

union & any

他们两个的区别可以理解为 前者 union 是或运算,而 any 是包含全部。

union:联合声明。|

// 1、变量声明字符串或数值类型
let all:string | number = 'zyugat'

// 2、数组声明多种类型
let all:(string | number | boolean)[]  = []
hd.push('zyugat.com',2010,true)

// 3、泛型方式声明
let all:Array<string|number|boolean>  = []
hd.push('zyugat.com',2010,true)

any:指包含所有值的顶部类型,任意值。

所以any不进行类型检查,等于关闭了 TS 对该变量的严格类型校验。

let all:any

//以下赋值不会报错
all='zyugat'
all=2010
all=true
all=[]
all={}
all=class{}

any的例子中,如果加入了 any 那么调用show 方法就不会报错。这是不好的,所以不要泛用 any。

class Hd {
    constructor() { }
}
const obj:any = new Hd;	// 没有报错终止,提示没有show方法。×
const obj = new Hd;	// 会报错终止,提示没有show方法。√
obj.show()

unknown

unknown:类型也是顶部类型这与 any 一样。unknown 赋值时要求明确类型,unknown 需要明确类型后赋值,any 则不需要。

用于表示未知的类型、unknown 进行 TS 的类型检查、需要 as 类型断言来转换类型。

let any: any = '文本'
let unknown: unknown = 'wenben'

let a: string = any
let b: string = unknown // 报错: 'unknown'未知类型不能赋值给'string'类型
let c: string = unknown as string

// 数值转换
let num: string = '99'
let d: number = num as unknown as number

void & never

void & never:区别点在于 void 可以有返回值但返回值必须是 null 或 undefined,而 never 不存在返回值。

void:值为 null 或undefined,

如果 TS 配置开启了 strictstrictNullChecks则不允许 void 为null,严格模式下只能是 undefined。。

function hd(): void {
  console.log('zyugat')
  return null
//  return 'zyugat'	// 会报错
}

never:是永远不会结束的函数,不存在返回值。


null & undefined

null & undefined:用于定义值为 null 或 undefined

当配置项启用 strictNullChecks 时,null 与 undefined 只能赋值给 void、null、undefined 类型(let hd:string =undefined; //配置strictNullChecks=true 时将报错

function getName(): string | null {
  return null
}
console.log(getName())

函数

let fun:Function
fun = ()=>'Hello World'
console.log(fun());

函数定义

函数定义有两种方式:函数声明定义 和 函数表达式

// 函数声明(Function Declaration)
function sum(x, y) {
  return x + y
}
// 函数表达式(Function Expression)
let mySum = function (x, y) {
  return x + y
}

其中,可以对函数进行输入输出约束

函数声明定义

// 函数声明定义
function sum(x: number, y: number): number {
    return x + y;
}

函数表达式定义

在TS中 => 是表示函数的定义,左边是输入类型,右边是输出类型。

定义1 中我仅对 输出类型 做了约束,没有对 输入类型 做约束,所以在 定义2 中我进行了补全。

// 函数表达式定义 1
let mySum = function (x: number, y: number): number {
  return x + y
}
// 函数表达式定义 2
let mySum: (x: number, y: number) => number = function (
  x: number,
  y: number
): number {
  return x + y
}

// 对象结构
type mySumFunc = (x: number, y: number) => number
// 函数定义
let mySum: mySumFunc = (x: number, y: number): number => {
  return x + y
}

重载定义

输入为数字的时候,输出也应该为数字,输入为字符串的时候,输出也应该为字符串。

重复定义了多次函数 reverse,前几次都是函数定义,最后一次是函数实现。在编辑器的代码提示中,可以正确的看到前两个提示。

function reverse(x: number): number
function reverse(x: string): string
function reverse(x: number | string): number | string | void {
  if (typeof x === 'number') {
    return Number(x.toString().split('').reverse().join(''))
  } else if (typeof x === 'string') {
    return x.split('').reverse().join('')
  }
}

参数类型

c:默认值参数可以不传,默认值是0.8。

d:可选参数可以不传,没有默认值。

function sum(a: number, b: number, c: number = 0.8, d?: number) {
  return a + b + c + d
}
console.log(sum(3, 3, 3, 3))

剩余参数

使用 ...rest 获取

function sum(...args: any[]): number {
  return args.reduce((s, n) => s + n, 0)
}

console.log(sum(1, 2, 3, 4, 5))

断言使用

  • as & const:断言 和 只读断言
  • 解构
    • 函数的返回值是个数组,包含字符串和函数。如果通过解构的方法获取,TS 无法判断他们的类型,所以会报错。
    • 通过断言的方式,指定类型。
  • 非空断言:声明该该不为空!
  • 枚举:enum

as & const

用户断定这是什么类型,不使用系统推断的类型。

as 类型
// 或
<类型>function hd(arg: number) {
  return arg ? 'name' : 2030
}

let f = hd(1) as string //let f: string
let f = <string>hd(1) //let f: string

const断言:只读断言

const hd = 'name' //const hd: "name"
let xj = 'name' //let xj: name

// 对象转换为只读属性
let user = { name: 'name' } as const

解构

函数的返回值是个数组,包含字符串和函数。如果通过解构的方法获取,TS 无法判断他们的类型,所以会报错。

调用时对返回值断言、在函数体内声明返回类型、使用 const

function foo() {
  let a = 'name'
  let b = (x: number, y: number): number => x + y
  return [a, b]
}
let [n, m] = foo()
// 报错:因为类型可能是字符串,所以不允许调用
m(1, 6)

// 断言 Function
console.log((m as Function)(1, 2))
// 断言 对象解构
console.log((m as (x: number, y: number) => number)(1, 5))

// 对 foo 函数的返回值 声明返回类型
let [n, m] = foo() as [string, (x: number, y: number) => number]
console.log(m(9, 19))

// 在函数体内声明返回类型
function foo() {
  let a = 'name'
  let b = (x: number, y: number): number => x + y
	return [a, b] as [typeof a, typeof b]
}

// 使用 as const
function foo() {
  let a = 'name'
  let b = (x: number, y: number): number => x + y
  return [a, b] as const
}
const [x, y] = foo() //变量 y 的类型为 () => void

同时也可以使用泛型的方式解决:具体看下面->泛型->解构


非空断言

获取的值可能是null,那么需要使用 as 断言类型或使用 ! 声明非null

const el: HTMLDivElement = document.querySelector('.bd') as HTMLDivElement
const el: HTMLDivElement = document.querySelector('.bd')!

Enums 枚举

枚举成员会被赋值为从 0 开始递增的数字,同时也会对枚举值到枚举名进行反向映射。

enum Days {
  Sun, // 0
  Mon, // 1
  Tue, // 2
  Wed, // 3
  Thu, // 4
  Fri, // 5
  Sat, // 6
}
console.log(Days["Sun"] === 0); // true
console.log(Days["Mon"] === 1); // true
console.log(Days["Tue"] === 2); // true
console.log(Days["Sat"] === 6); // true

console.log(Days[0] === "Sun"); // true
console.log(Days[1] === "Mon"); // true
console.log(Days[2] === "Tue"); // true
console.log(Days[6] === "Sat"); // true

当某个字段设置数值类型的值后,后面的在它基础上递增

enum Days {
  Sun=10, // 10
  Mon, // 11
  Tue, // 12
  Wed, // 13
  Thu, // 14
  Fri, // 15
  Sat, // 16
}

常数枚举:使用 const enum 定义的枚举类型

区别在于,它会在编译阶段被删除,并且不能包含计算成员。

const enum Directions {
  Up,
  Down,
}

let directions = [Directions.Up, Directions.Down]

// 编译后:
var directions = [0 /* Up */, 1 /* Down */];

外部枚举(Ambient Enums)是使用 declare enum 定义的枚举类型:

只会用于编译时的检查,编译结果中会被删除。

declare enum Directions {
  Up,
  Down,
}
let directions = [Directions.Up, Directions.Down]
// 编译后:
var directions = [0 /* Up */, 1 /* Down */];

  • 类的定义
  • 修饰符
  • 类的属性
    • constructor、static、get/set、abstract

类的定义

通过约束数组的类型为User,使其成员只能是 User 类型对象

class User {
  name: string
  age: number
  constructor(n: string, a: number) {
    this.name = n
    this.age = a
  }

  info(): string {
    return `${this.name}的年龄是 ${this.age}`
  }
}

const u2 = new User('name1', 12)
const u1 = new User('name2', 18)

const users: User[] = [u1, u2]
console.log(users)
// [ User { name: 'name2', age: 18 }, User { name: 'name1', age: 12 } ]

修饰符

  • public:公开的、类的内部与外部都可以修改和访问

  • protected:受保护的、只允许在父类与子类使用

  • private:私有的、只能在本类中使用

  • readonly:只读、不能修改。类型 const

class User {
  public name: string
  protected age: number
  constructor(n: string, a: number) {
    this.name = n
    this.age = a
  }

  public info(): string {
    return `${this.name}的年龄是 ${this.age}`
  }
}

类的属性

constructor、static、get/set、abstract

constructor:构造函数,在ts中可用于定义属性,简化代码量。

class User {
  constructor(public name: string, public age: number) {}

  public info(): string {
    return `${this.name}的年龄是 ${this.age}`
  }
}

static:定义静态属性,属于构造函数并且所有对象都可以共享。

因为属于构造函数,所以可以直接访问。

class hello {
  static show(): string {
    return `Hello World`
  }
}
console.log(hello.show())

单例模式:当 constructor 构造函数定义为非 public 时,无法实例化对象。那么可以通过该特性结合 static 实现单例,只实例化一个对象。

class hello {
  // 受保护的构造函数
  protected constructor() {}
  static show(): string {
    return `Hello World`
  }
}
const h = hello.show()

get/set

class User {
  private _name
  constructor(name: string) {
    this._name = name
  }
  public get name() {
    return this._name
  }
  public set name(value) {
    this._name = value
  }
}

const u = new User('name1')
u.name = 'name2'
console.log(u.name)

abstract:抽象类。

  • 抽象方法必须存在于抽象类中。
  • 子类必须实现这个抽象方法。
  • 不可以直接使用,只能被继承。
abstract class School {
  // 抽象方法必须存在于抽象类中
  abstract name: string
  abstract move(): void
}
class Student extends School {
  // 子类必须实现抽象方法
  name: string = '学生'
  public move(): void {}
}
// const s = new School() // 报错,不能通过抽象方法创建实例

接口

如果尝试添加一个接口中不存在的属将报错,移除接口的属性也将报错。

  • Interface
    • 同名会继承
    • 只读:readonly
    • 额外属性:必须包含整个接口的全部类型[key: string]: any
    • 约束对象、数组、类。约束函数的:函数声明、参数列表、返回值、构造函数的参数
  • Type
    • 同名不会继承,Interface 可以使用 extends 继承 type。Type 继承 Interface 需使用合并运算符 &
    • 合并需要使用 &,或运算,满足任意一个。|
  • 总结:接口能用Interface就用他。

接口可以使用 Interfacetype定义

其中 interface 会将同名接口进行合并。type不能

interface User {
  name: string
}
interface User {
  age: number
}
const hd: User = {
name: 'NAME',
age: 18,
}

Interface

如果尝试添加一个接口中不存在的属将报错,移除接口的属性也将报错。

Interface:接口,

接口和类间结合使用:implements

接口和接口间继承使用:extends

接口约束函数/对象/数组使用::

interface AnimationInterface {
  name: string
  move(): void
}

只读:readonly

做为变量使用的话用const,若做为属性则使用readonly

interface Person {
	readonly id: number;
}

格外属性:[key: string]: string

如果使用 string 有一个问题那就是一旦定义了任意属性,那么确定属性和可选属性的类型都必须是它的类型的子集。什么意思呢?就是额外属性必须包含整个接口的全部类型

例如->下面案例中,任意属性的值允许是 string,但是可选属性 age 的值却是 numbernumber 不是 string 的子属性,所以报错了。

因此可以使用:[propName: string]: any;

interface Person {
    name: string;
    age?: number;
    [propName: string]: string;
    // [propName: string]: string | number;
  	// [propName: string]: any;
}

let tom: Person = {
    name: 'Tom',
    age: 25,
    gender: 'male'
};

// index.ts(3,5): error TS2411: Property 'age' of type 'number' is not assignable to string index type 'string'.
// index.ts(7,5): error TS2322: Type '{ [x: string]: string | number; name: string; age: number; gender: string; }' is not assignable to type 'Person'.
//   Index signatures are incompatible.
//     Type 'string | number' is not assignable to type 'string'.
//       Type 'number' is not assignable to type 'string'.

约束类

class Point {
    x: number;
    y: number;
    constructor(x: number, y: number) {
        this.x = x;
        this.y = y;
    }
}

function printPoint(p: Point) {
    console.log(p.x, p.y);
}

printPoint(new Point(1, 2));

约束函数的参数列表和返回值

约束函数的参数列表和返回值。

interface UserInterface {
  name: string
  age: number
  isLock: boolean
}
function lockUser(user: UserInterface, state: boolean): UserInterface {
  user.isLock = state
  return user
}
let user: UserInterface = {
  name: '我',
  age: 18,
  isLock: false,
}
lockUser(user, true)
console.log(user)

约束函数的声明

interface Pay {
  // 参数 price 必须是数字。返回值必须是 boolean
  (price: number): boolean
}
const getUserInfo: Pay = (price: number) => true

约束函数的构造函数

将 接口 作为参数的 约束。

interface UserInterface {
  name: string
  age: number
}
class User {
  // 公开 info 对象
  info: UserInterface
  constructor(user: UserInterface) {
    this.info = user
  }
}
const u = new User({ name: 'name', age: 18 })
console.log(u)

type

type 与 interface 都可以描述一个对象或者函数,使用 type 用于定义类型的别名。

约束对象的方式和接口一样,我这里就不一一赘述了,只对 不同点 进行说明。

type可以声明基本类型别名、联合类型、元祖等类型

// 基本类型别名
type Name = string

// 联合类型
interface Dog {
  wong()
}
interface Cat {
  miao()
}
type Pet = Dog | Cat

// 具体定义数组每个位置的类型
type PetList = [Dog, Pet]

type语句中还可以使用typeof获取实例的类型进行赋值

// 当你想要获取一个变量的类型时,使用typeof
let div = document.createElement('div')
type B = typeof div

type其他骚操作

type StringOrNumber = string | number;
type Text = string | { text: string };
type NameLookup = Dictionary<string, Person>;
type Callback<T> = (data: T) => void;
type Pair<T> = [T, T];
type Coordinates = Pair<number>;
type Tree<T> = T | { left: Tree<T>, right: Tree<T> };

声明继承时

  • interface 会将同名接口进行合并。type 同名不会继承。
  • interface 可以 extends 继承 type。type 继承 interface 需使用 &
  • type 合并需要使用 &
  • type 或运算,满足任意一个。|
type Name = {
  name: string
}
type Age = {
  age: number
}

// interface 可以 extends 继承 type
interface User1 extends Name {
  age: number
}

// type 继承 interface
type User2 = User1 & {
  key: string
}

// 合并 type
type User3 = Name & Age
// 或运算
type User4 = Name | Age

泛型 Generics

泛型指使用时才定义类型,即类型可以像参数一样定义,主要解决类、接口、函数的复用性,让它们可以处理多种类型。

我们在函数名后添加了 <T>,其中 T 用来指代任意输入的类型,在后面的输入 arg: T 和输出 T 中即可使用了。

function dump<T>(arg: T): T {
  return arg
}
let g = dump<string>('NAME')

类型继承

我要设计一个函数,获取长度。

第一步、如果直接,会报错,类型“T”上不存在属性“length”

function getLength<T>(arg: T): number {
    return arg.length;
}

第二步:使 T 类型(泛型)继承一个 string。缺点是只能处理字符串的数据。

function getLength<T extends string>(arg: T): number {
  return arg.length
}

第三步:使 T 类型(泛型)定义时就包含 length 属性。这样就能处理 字符串和数组 。

function getLength<T extends { length: number }>(arg: T): number {
  return arg.length
}

console.log(getLength('baidu.com')) // 9
console.log(getLength(['NAME'])) // 4
// console.log(getLength(18)) // undefined

// 如果只是字符串和数组可以使用联合类型
function getLength<T extends string | any[]>(arg: T): number {}

// 参数是 T[] 就存在 length,缺点是只能处理数组
function getLength<T>(arg: T[]): number{}

第四步:返回元组

function getLength<T extends string>(arg: T): [T, number] {
  return [arg, arg.length]
}

第三步中补充一点,可以使用接口

interface Lengthwise {
  length: number
}
function getLength<T extends Lengthwise>(arg: T): number {
  return arg.length
}

泛型复用类

创建一个 可复用的类。下面案例中,collections对应number类型。userCollection对应对象类型。

class Collection<T> {
  data: T[] = []
  public push(...items: T[]) {
    this.data.push(...items)
  }
  public shift() {
    return this.data.shift()
  }
}
// 测试 number 类型
const collections = new Collection<number>()
collections.push(1)

// 测试 Object 类型
type User = { name: string; age: number }
const da: User = { name: 'NAME', age: 18 }
const userCollection = new Collection<User>()

userCollection.push(da)
console.log(userCollection.shift())

接口结合泛型

同上。

class User<T> {
  constructor(protected _user: T) {}
  public get(): T {
    return this._user
  }
}

interface UserInterface {
  name: string
  age: number
}
const instance = new User<UserInterface>({ name: 'NAME', age: 18 })
console.log(instance.get().age)

接口

接口的 comments 是一个数组,然后评论类型CommentType是一个对象,那么他就是一个*对象数组**。

// 文章接口
interface articleInterface<T, B> {
  title: string
  isLock: B
  comments: T[]
}

// 评论类型
type CommentType = {
  comment: string
}

// 定义文章数据包含评论内容
const a: articleInterface<CommentType, boolean> = {
  title: '评论标题',
  isLock: true,
  comments: [{ comment: '这是一个评论' }],
}

console.log(a)

解构

通过泛型解决解构,得到具体的值类型,获地更安全的代码,同时会有更好的代码提示。

function foo() {
  const name: string = 'NAME'
  const age: number = 19
  return fo(name, age)
}
function fo<T extends any[]>(...args: T): T {
  return args
}
const [l, r] = foo()

装饰器

装饰器(Decorators)为我们在类的声明及成员上通过编程语法扩展其功能,装饰器以函数的形式声明。

通过:@ 调用

可用装饰器包括以下几种

装饰器说明
ClassDecorator类装饰器
MethodDecorator方法装饰器
PropertyDecorator属性装饰器
ParameterDecorator参数装饰器

开启装饰器

"experimentalDecorators": true,
"emitDecoratorMetadata": true

如果报错Unable to resolve signature of method decorator when called as an expression.

tsc 1.ts --target ES5 -w --experimentalDecorators

类装饰器

首先执行 RoleDecorator 装饰器,然后执行类的构造函数。

语法糖:NameDecorator(Class)

  • 参数:构造函数
// 箭头函数
const MoveDecorator: ClassDecorator = (constructor: Function): void => {
  console.log(`装饰器 RoleDecorator `)
}

// function函数
function MoveDecorator(constructor: Function): void {
  console.log(`装饰器 RoleDecorator `)
}

@MoveDecorator
class Tank {
  constructor() {
    console.log('tank 构造函数')
  }
}
new Tank()
// 装饰器 RoleDecorator 
// tank 构造函数

// 语法糖
MoveDecorator(Tank)

原型对象

我给 Decorator 的原型对象上添加方法或属性,在类中我们也需要添加默认属性。

我们可以选择为类添加默认属性解决,也可以在调用时使用断言处理。

const MoveDecorator: ClassDecorator = (constructor: Function) => {
  constructor.prototype.name = 'NAME'
  constructor.prototype.getPosition = (): { x: number; y: number } => {
    return { x: 100, y: 100 }
  }
}

@MoveDecorator
class Tank {
  public name: string | undefined
  public getPosition() {}
  constructor() {
    console.log('tank 构造函数')
  }
}

const tank = new Tank()
console.log((tank as any).getPosition());
//或使用以下方式断言
console.log((<any>tank).getPosition());

消息处理

在 装饰器 中定义一个属性 message 类中设置该属性的值,装饰器就会被触发从而打印出该值。

//消息响应
const MessageDecorator: ClassDecorator = (constructor: Function): void => {
  constructor.prototype.message = (message: string): void => {
    console.log(message)
  }
}

@MessageDecorator
class LoginController {
  public login() {
    console.log('登录逻辑')
    this.message('登录成功')
  }
}
const controller = new LoginController()
controller.login()
// 登录逻辑
// 登录成功

工厂装饰器

使用 switch 根据条件返回不同的装饰器。

const MusicDecorator = (type: string): ClassDecorator => {
  switch (type) {
    case 'player':
      return (constructor: Function) => {
        constructor.prototype.playMusic = (): void => {
          console.log(`播放音乐 player`)
        }
      }
      break
    default:
      return (constructor: Function) => {
        constructor.prototype.playMusic = (): void => {
          console.log(`播放音乐 default`)
        }
      }
  }
}

@MusicDecorator('tank')
class Tank {
  constructor() {}
}
const tank = new Tank()
;(<any>tank).playMusic()

@MusicDecorator('player')
class Player {}

const player: Player = new Player()
;;(player as any).playMusic()

方法装饰器

装饰器也可以修改方法,首先介绍装饰器函数参数说明

参数说明
参数一普通方法是构造函数的原型对象 Prototype,静态方法是构造函数
参数二方法名称
参数三属性描述

如果报错Unable to resolve signature of method decorator when called as an expression.则执行下面。

tsc 1.ts --target ES5 -w --experimentalDecorators

简单使用

先保存原型方法,然后在描述中调用它。

/*
const ShowDecorator: MethodDecorator = (...args: any[]) => {
  let target = args[0]
  let propertyKey = args[1]
  let descriptor = args[2]
  const [, , descriptor] = args
}
*/

const ShowDecorator: MethodDecorator = (
  target: Object,
  propertyKey: string | symbol,
  descriptor: PropertyDescriptor
): void => {P
  // 保存原型方法
  let originalMethod = descriptor.value
  descriptor.value = () => {
    // 调用原型方法
    console.log('show', originalMethod())
  }
}

class dec {
  @ShowDecorator
  show() {
    return 'desc'
  }
}

new dec().show()

// show desc

自定义内容

保存在 装饰器参数 中。

const ShowDecorator = (message: string): MethodDecorator => {
  return (
    target: Object,
    propertyKey: string | symbol,
    descriptor: PropertyDescriptor
  ) => {
    descriptor.value = () => {
      console.log(message)
    }
  }
}

class dec {
  @ShowDecorator('自定义内容')
  store() {
    console.log('保存文章')
  }
}
new dec().store()
// 自定义内容

登录验证

//用户类型
type userType = { name: string; isLogin: boolean; permissions: string[] }
//用户数据
const user: userType = {
  name: 'NAME',
  isLogin: true,
  permissions: ['store', 'manage'],
}
//权限验证装饰器工厂
const AccessDecorator = (keys: string[]): MethodDecorator => {
  return (
    target: Object,
    propertyKey: string | symbol,
    descriptor: PropertyDescriptor
  ) => {
    const method = descriptor.value
    const validate = () =>
      // 验证用户权限是否一致
      keys.every(k => {
        return user.permissions.includes(k)
      })
    descriptor.value = () => {
      if (user.isLogin === true && validate()) {
        alert('验证通过')
        return method()
      }
      alert('验证失败')
    }
  }
}

class Article {
  show() {
    console.log('显示文章')
  }
  @AccessDecorator(['store', 'manage'])
  store() {
    console.log('保存文章')
  }
}
new Article().store()

属性装饰器

参数说明
参数一普通方法是构造函数的原型对象 Prototype,静态方法是构造函数
参数二属性名称
const PropDecorator: PropertyDecorator = (
  target: Object,
  propertyKey: string | symbol
): void => {
  console.log(target, propertyKey)
}

class dec {
  @PropDecorator
  public name: string | undefined = 'NAME'
}

大写转小写

const PropDecorator: PropertyDecorator = (
  target: Object,
  propertyKey: string | symbol
): void => {
  let value: string
  const getter = () => {
    return value
  }
  const setter = (v: string) => {
    value = v.toLowerCase()
  }

  Object.defineProperty(target, propertyKey, {
    set: setter,
    get: getter,
  })
}

class dec {
  @PropDecorator
  public name: string | undefined
}

const instance = new dec()
instance.name = 'BAIdU'
console.log(instance.name)

参数装饰器

可以对方法的参数设置装饰器,参数装饰器的返回值被忽略。

参数说明
参数一普通方法是构造函数的原型对象 Prototype,静态方法是构造函数
参数二方法名称
参数三参数所在索引位置
const ParameterDecorator: ParameterDecorator = (
  target: any,
  propertyKey: string | Symbol,
  parameterIndex: number
): void => {
  console.log(target, propertyKey, parameterIndex)
}

class dec {
  show(name: string, @ParameterDecorator url: string) {}
}

元数据

yarn add reflect-metadata

为对象中的属性,添加 元数据描述。

defineMetadata(元数据名, 元数据内容, 对象, 属性)

getMetadata(元数据名, 对象, 属性)

//引入支持元数据的扩展名
import 'reflect-metadata'
const meta = { name: 'NAME', city: '广州' }
//在对象 meta 的属性 name 上定义元数据 (元数据指对数据的描述)
Reflect.defineMetadata('DESC', 'NAMEDESC', meta, 'name')
let value = Reflect.getMetadata('DESC', meta, 'name')
console.log(value)

模块

  • 导出
    • 简单的导出,对导出部分重命名。
    • 重新导出,只导出那个模块的部分内容,使用一个模块包含多个模块一起导出。
  • 导入
    • 简单的导入,重命名导入。导入到一个变量。

导出

任何声明(比如变量,函数,类,类型别名或接口)都能够通过添加export关键字来导出

StringValidator.ts

export interface StringValidator {
    isAcceptable(s: string): boolean;
}

简单的导出,同时还可以对导出的部分重命名。

ZipCodeValidator.ts

import { StringValidator } from "./StringValidator";

export const numberRegexp = /^[0-9]+$/;

class ZipCodeValidator implements StringValidator {
    isAcceptable(s: string) {
        return s.length === 5 && numberRegexp.test(s);
    }
}
export { ZipCodeValidator };
// 对导出的部分重命名
export { ZipCodeValidator as mainValidator };

重新导出

只导出那个模块的部分内容。

ParseIntBasedZipCodeValidator.ts

export class ParseIntBasedZipCodeValidator {
    isAcceptable(s: string) {
        return s.length === 5 && parseInt(s).toString() === s;
    }
}

// 导出原先的验证器但做了重命名
export {ZipCodeValidator as RegExpBasedZipCodeValidator} from "./ZipCodeValidator";

使用一个模块包含多个模块,一起导出

AllValidators.ts

export * from "./StringValidator"; // exports 'StringValidator' interface
export * from "./ZipCodeValidator";  // exports 'ZipCodeValidator' and const 'numberRegexp' class
export * from "./ParseIntBasedZipCodeValidator"; //  exports the 'ParseIntBasedZipCodeValidator' class
                                                 // and re-exports 'RegExpBasedZipCodeValidator' as alias
                                                 // of the 'ZipCodeValidator' class from 'ZipCodeValidator.ts'

导入

使用以下import形式之一来导入其它模块中的导出内容。

import { ZipCodeValidator } from "./ZipCodeValidator";
// 重命名
import { ZipCodeValidator as ZCV } from "./ZipCodeValidator";

let myValidator = new ZipCodeValidator();
let myValidator = new ZCV();

将整个模块导入到一个变量,并通过它来访问模块的导出部分

import * as validator from "./ZipCodeValidator";
let myValidator = new validator.ZipCodeValidator();

export =import = require()

若使用export =导出一个模块,则必须使用TypeScript的特定语法import module = require("module")来导入此模块。

// ZipCodeValidator.ts
class ZipCodeValidator {
  // ...
}
export = ZipCodeValidator;

// Test.ts
import zip = require("./ZipCodeValidator");
let validator = new zip();

namespace:数据需要 export 导出才能使用。

namespace User {
  export let name: string = 'NAME'
}
namespace User2 {
  export let name: string = 'NAME2'
}
console.log(User.name)

namespace User {
  export let name: string = 'NAME'
  export namespace User2 {
    export let name: string = 'NAME2'
  }
}
console.log(User.User2.name)

别名

import else = User.name

合并打包1

tsc --outFile ./dist/app.js user.ts index.ts

合并打包2

reference:在 index.ts 中引入依赖文件

/// <reference path="user.ts"/>
console.log(User.name);
tsc --outFile ./dist/app.js index.ts 

amd 模块打包

第一步:修改配置项->"module": "amd"

第二部:导入 import { User } from './user.js'

编译

tsc --outFile ./dist/app.js index.ts
<script src="dist/app.js"></script>
<script>
  require(['App'])
  require(['User'], User => {
    console.log(User.title)
  })
</script>

Pro

高级类型

交叉类型、联合类型、、

**交叉类型使用范围:我希望一个类型对象同时拥有 多种类型 的成员。**理解为:&

第一步:首先 extend 函数有两个泛型参数 First,Second。前者传入 Person类 ,后者传入 ConsoleLogger 的属性

Person类 的作用是通过构造函数设置 name属性。

ConsoleLogger.prototype 的作用是 传入log属性 打印 name 值。

第二步:第二行代码,Partial<Type> 将它所有的属性设置为 可选的。(可以理解为 result 要么是First 要么是 Second)

第三步:3到7行,遍历 对象属性,通过 hasOwnProperty 判断 prop 是否是 first 的属性。如果是那就通过断言获取。

function extend<First, Second>(first: First, second: Second): First & Second {
    const result: Partial<First & Second> = {};
    for (const prop in first) {
        if (first.hasOwnProperty(prop)) {
            (result as First)[prop] = first[prop];
        }
    }
    for (const prop in second) {
        if (second.hasOwnProperty(prop)) {
            (result as Second)[prop] = second[prop];
        }
    }
    return result as First & Second;
}

class Person {
    constructor(public name: string) { }
}

interface Loggable {
    log(name: string): void;
}

class ConsoleLogger implements Loggable {
    log(name) {
        console.log(`Hello, I'm ${name}.`);
    }
}

const jim = extend(new Person('Jim'), ConsoleLogger.prototype);
jim.log(jim.name);

联合类型:传入numberstring类型的参数

function padLeft(value: string, padding: string | number) {
    // ...
}

类型守卫与类型区分

对象同时拥有多个类型,我们无法确定 这个类型 是谁的时候。可以通过类型断言。

当把 pet 断言成 Fish,判断是否存在 swim 方法,如果不存在那就说明不是这个类型。

let pet = getSmallPet();

if ((pet as Fish).swim) {
    (pet as Fish).swim();
} else if ((pet as Bird).fly) {
    (pet as Bird).fly();
}

用户自定义类型守卫

使用类型判定:parameterName is Type

function isFish(pet: Fish | Bird): pet is Fish {
    return (pet as Fish).swim !== undefined;
}
if (isFish(pet)) {
    pet.swim();
}
else {
    pet.fly();
}

使用in操作符:其中n是字符串字面量或字符串字面量类型且x是个联合类型

通过 in 判断 pet 是否存在 swim

function move(pet: Fish | Bird) {
    if ("swim" in pet) {
        return pet.swim();
    }
    return pet.fly();
}

typeof v === "typename"类型守卫:判断类型 是否是 原始类型

"typename"必须为:"number""string""boolean""symbol"

function padLeft(value: string, padding: string | number) {
    if (typeof padding === "number") {
        return Array(padding + 1).join(" ") + value;
    }
    if (typeof padding === "string") {
        return padding + value;
    }
    throw new Error(`Expected string or number, got '${padding}'.`);
}

instanceof:通过构造函数细化类型,左边对象右边函数,沿着原型链查找。

interface Padder {
  	// ...
}
class SpaceRepeatingPadder implements Padder {
  	// ...
}
class StringPadder implements Padder {
  	// ...
}
function getRandomPadder() {
    return Math.random() < 0.5 ?
        new SpaceRepeatingPadder(4) :
        new StringPadder("  ");
}

// 类型为SpaceRepeatingPadder | StringPadder
let padder: Padder = getRandomPadder();

if (padder instanceof SpaceRepeatingPadder) {
    padder; // 类型细化为'SpaceRepeatingPadder'
}
if (padder instanceof StringPadder) {
    padder; // 类型细化为'StringPadder'
}

注意事项

应该使用numberstringbooleansymbol类型。

/* 正确 */
function reverse(s: string): string;

不要定义没有使用过类型参数的泛型类型。

请尽量不要使用any类型,除非你正在将 JavaScript 代码迁移到 TypeScript 代码。

回调函数的返回值类型,应该为返回值会被忽略的回调函数设置返回值类型void,而不是any:

/* 错误 */
function fn(x: () => any) {
    x();
}
/* 正确 */
function fn(x: () => void) {
    x();
}

不要在回调函数里使用可选参数,除非这是你想要的:

/* 错误 */
interface Fetcher {
    getObject(done: (data: any, elapsedTime?: number) => void): void;
}
/* 正确 */
interface Fetcher {
    getObject(done: (data: any, elapsedTime: number) => void): void;
}

函数重载

不要把模糊的重载放在具体的重载前面:

/* 错误 */
declare function fn(x: any): any;
declare function fn(x: HTMLElement): number;
declare function fn(x: HTMLDivElement): string;

var myElem: HTMLDivElement;
var x = fn(myElem); // x: any, wat?

/* 正确 */
declare function fn(x: HTMLDivElement): string;
declare function fn(x: HTMLElement): number;
declare function fn(x: any): any;

var myElem: HTMLDivElement;
var x = fn(myElem); // x: string, :)

配置

配置说明
noImplicitAny禁止使用隐含的 any 类型,如函数参数没有设置具体类型
strictNullChecks开启时不否允许将 null、undefined 赋值给其他类型比如字符串
target转换成JS 的版本
strict是否严格模式执行
module使用的模块系统
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值