Appearance
Typescript 扫盲
简介
- 兼容性:
.js文件可以直接重命名为.ts - 主要提供了类型系统和对 ESnext 的支持,第三方库可以编写单独的类型文件供 TypeScript 读取
- 编译报错时终止 js 文件的生成 -> 可以在 tsconfig.json 中配置
noEmitOnError(如不配置即使静态编译报错也能生成 js)
基础
基本类型
boolean Boolean | number Number | string String | null | undefined
ts
let isDone: boolean = true // 原始数据类型
let createB: Boolean = new Boolean(true) // 对象数据类型
// void 表示没有任何返回值的函数
function alertName(): void {
alert('My name is Tom')
}
// 申明 void 变量只能赋值 undefined null 但是
let unusable: void = undefined
// undefined 和 null 是所有类型的子类型
let u: undefined = undefined
let num: number = undefined // 但是 void 类型的变量不能赋值给 numberany
允许赋值为任意类型、访问任何属性
ts
// 对它的任何操作,返回的内容的类型都是任意值
let anyThing: any = '1212'类型推导
定义变量时赋值 -> 会根据类型推导的规则推导出一个类型,如果定义时没有赋值 -> 将被识别为任意类型 any
ts
let myFavoriteNumber = 'seven'
myFavoriteNumber = 7 // 编译报错
let myFavoriteNumber
myFavoriteNumber = 'seven'
myFavoriteNumber = 7 // OK 的联合类型
表示取值可以为多种类型中的一种 string | number
ts
let myFavoriteNumber: string | number
// 在还没确定类型时 -> 只能访问 联合类型 的所有类型里共有的属性
myFavoriteNumber.length // 编译报错
myFavoriteNumber.toSting() // OK 的
// 赋值后会推导出类型 -> 可以访问相应的属性
myFavoriteNumber = 'seven'
myFavoriteNumber = 7对象的类型 - 接口
- 接口是对类(classes)的一部分行为进行抽象,也能对对象的形状(Shape)进行描述
- 有一些内置对象的接口定义
IArguments,NodeList,HTMLCollection
ts
// 接口一般首字母大写
interface Person {
readonly id: number
name: string
age?: number
[propName: string]: any
}
// - readonly 表示该属性是只读的,定义对象之后,不能修改该属性
// - age 后的 ? 表示该属性是可选的
// - [propName: string] 定义了可以有任意 string 类型的属性 key
// - 同时确定了该接口的确定属性和可选属性的值都必须是它的类型(any)的子集
// 约束对象的 Shape
let tom: Person = {
id: 89757,
name: 'Tom',
age: 25,
gender: 'male',
}数组的类型
- 「类型 + 方括号」表示法
ts
let fibonacci: number[] = [1, 1, 2, 3, 5]
// [] 元素是 any
let list: any[] = ['tangdw', 25, { website: 'http://tangdw.com' }]- 数组泛型法
ts
let fibonacci: Array<number> = [1, 1, 2, 3, 5]- 接口表示法
ts
interface NumberArray {
[index: number]: number
}
// 类似对象 key 是 number value 也是 number
let fibonacci: NumberArray = [1, 1, 2, 3, 5]函数的类型
ts
// 定义了参数个数、类型,返回类型, ? 表示这个参数是可选的(放在最后面
function sum(x: number, y: number, z?: string): number {
return x + y
}
// 实际上是对右边的匿名函数定义了类型,而 sum 的类型是推导出来的
let sum = function (x: number, y: number): number {
return x + y
}
// sum: (x: number, y: number) => number 这样才是对 sum 的定义 -> -_- 麻烦了一点
let sum: (x: number, y: number) => number = function (x: number, y: number): number {
return x + y
}
// 亦可以用接口来定义
interface GetSum {
(x: number, y: number): number
}
let sum: GetSum
sum = function (x: number, y: number): number {
return x + y
}
// 剩余参数 ...items 可以用 any[] 类型来定义它
function push(array: any[], ...items: any[]) {
items.forEach(function (item) {
array.push(item)
})
}
// 重载
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('')
}
}
// ts 优先从最前面的函数定义开始匹配,所以多个函数定义如果有包含关系,需要优先把精确的定义写在前面
// 这样的函数输入可以是 number 或 string,输入 number 就返回 number,输入 string 就返回 string类型断言
<类型>变量 / 变量 as 类型 (在 tsx 中只能用后一种
ts
function getLength(something: string | number): number {
// 这里用 <string> 断言 something 不然编译会报错(因为 length 不是 string | number 的共有属性
if ((<string>something).length) {
return (<string>something).length
} else {
return something.toString().length
}
}ts
// Type 'readonly [10, 20]' 只读类型
let y = [10, 20] as const声明文件
使用第三方库时,我们需要引用它的声明文件,才能获得对应的代码补全、接口提示等功能 @types/xxx
ts
// src/jQuery.d.ts
// declare 声明语句 -> 声明 window 下的全局变量
declare var jQuery: (selector: string) => any
// >> 一般来说,全局变量都是一个常量,不允许再修改它的值了,所以不会用 var 或 let 都用 const
declare function jQuery(selector: string): any
// namespace 用于指定命名空间(对象
declare namespace jQuery {
// 可以直接使用 interface 或 type 来声明一个全局的接口或类型(前面不需要 declare
interface AjaxSettings {
method?: 'GET' | 'POST'
data?: any
}
function ajax(url: string, settings?: AjaxSettings): void
}
// 以上声明会不冲突的合并起来 -_-
// 导出
// 只有 function、class 和 interface 可以直接默认导出,其他的变量需要先定义出来,再默认导出声明文件 -> 把声明语句放到一个单独的文件(jQuery.d.ts)中 | 书写声明文件
ts
// src/index.ts
jQuery('#foo')
// jQuery. 声明文件的命名空间
let settings: jQuery.AjaxSettings = {
method: 'POST',
data: {
name: 'foo',
},
}
jQuery.ajax('/api/post_something', settings)内置对象
BooleanErrorDateRegExpMDNDOM BOM 的内置对象
DocumentHTMLElementEventNodeListlibTypeScript 自动包含了核心 lib 无需引入,参数类型自动推断
但是 node 的没包含 需要
npm install @types/node --save-dev
进阶
类型别名 给一个类型起个新名字
type 自定义类型(别名
ts
type Name = string
type NameResolver = () => string
type NameOrResolver = Name | NameResolver
function getName(n: NameOrResolver): Name {
if (typeof n === 'string') {
return n
} else {
return n()
}
}字符串字面量类型
type 自定义类型
ts
// 用 type 定义 EventNames 类型 只能是这三种字符串中的一个
type EventNames = 'click' | 'scroll' | 'mousemove'
function handleEvent(ele: Element, event: EventNames) {
// .
}
handleEvent(document.getElementById('world'), 'dbclick') // 报错,event 不能为 'dbclick'
handleEvent(document.getElementById('hello'), 'scroll') // OK 的元组
相对于数组来说,是一个由不同类型元素组合组成的集合
ts
// 初始化可以不赋值,但是赋值必须提供所有元组类型中指定的项
let xcatliu: [string, number] = ['Xcat Liu', 25]
// 后面再追加元素时(越界),只能加已定义的联合类型
xcatliu.push(true) // 编译报错枚举 Enum
取值被限定在一定范围内,比如一周只能有七天、颜色限定为红绿蓝
ts
enum Days {
Sun,
Mon,
Tue,
Wed,
Thu,
Fri,
Sat,
}
// 成员会被赋值为从 0 开始递增的数字,同时也会对 枚举值 -> 枚举名 进行反向映射
Days['Mon'] === 1 // true
Days[1] === 'Mon' // true
// 给枚举项手动赋值
enum Days {
Sun = 3,
Mon = 1,
Tue,
Wed,
Thu,
Fri,
Sat = 'red'.length,
}
// 其中未被赋值的 Tue Wed 依次递增(步长 1) 2 3
// Wed 3 覆盖前面手动定义的 Sun 不会报错,但是最好避免这种情况
// Sat 计算所得项,如果它后面是一个未手动赋值的成员 -> 编译报错- 常数枚举
const enum在编译阶段会被删除,不能包含计算成员
ts
const enum Directions {
Up,
Down,
Left,
Right,
}
let directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right]
// 👇
var directions = [0 /* Up */, 1 /* Down */, 2 /* Left */, 3 /* Right */]- 外部枚举
declare enum常出现在声明文件xx.d.ts中
类
在 ES5 通过构造函数实现类的概念,通过原型链实现继承。ts 在 ES6 class 之外还添加了一些新的用法
- ES6 存取器 使用 getter 和 setter 可以改变属性的赋值和读取行为
js
class Animal {
constructor(name) {
this.name = name
}
get name() {
return 'Jack'
}
set name(value) {
console.log('setter: ' + value)
}
}
let a = new Animal('Kitty') // setter: Kitty
a.name = 'Tom' // setter: Tom
console.log(a.name) // Jackts 修饰符
publicprivateprotectedpublic公有的,可以在任何地方被访问到。默认所有的属性方法都是public的private私有的,不能在声明它的类之外访问protected受保护的,和private类似,区别是它在子类中中是允许被访问的
抽象类
abstract
ts
// 声明抽象类,不允许被实例化
abstract class Animal {
public name
public constructor(name) {
this.name = name
}
public abstract sayHi()
}
let a = new Animal('Jack') // 编译报错
// 由子类去继承并实现抽象方法
class Cat extends Animal {
// 报错...不允许的
// public eat() {
// console.log(`${this.name} is eating.`);
// }
public sayHi() {
console.log(`Meow, My name is ${this.name}`)
}
}
// OK 的
let cat = new Cat('Tom')类与接口
实现implements 一个类去实现某个接口
ts
interface Alarm {
alert()
}
interface Light {
lightOn()
lightOff()
}
// 接口继承接口
interface LightableAlarm extends Alarm {
lightOn()
lightOff()
}
class Door {}
class SecurityDoor extends Door implements Alarm {
alert() {
console.log('SecurityDoor alert')
}
}
// 实现多个和接口用 , 分隔
class Car implements Alarm, Light {
alert() {
console.log('Car alert')
}
}
// 接口继承类
interface LangCar extends Car {
len: number
}泛型
在定义接口、函数或类的时候,不预先指定具体的类型,而是在使用的时候再指定类型
ts
// 在函数名后添加 <T> 其中 T 代表任意输入的类型 (value)
function createArray<T>(length: number, value: T): Array<T> {
// T[] <-> Array<T> 泛型
let result: T[] = []
for (let i = 0; i < length; i++) {
result[i] = value
}
return result
}
createArray<string>(3, 'x') // ['x', 'x', 'x']
// 也可以不指定 T 让类型推导自动推导出来 -_-
createArray(3, 'x') // ['x', 'x', 'x']一次定义多个类型参数
ts
// tuple: [T, U] 定义形参 tuple 是一个元组类型
function swap<T, U>(tuple: [T, U]): [U, T] {
return [tuple[1], tuple[0]]
}
swap([7, 'seven']) // ['seven', 7]泛型约束
js
interface Lengthwise {
length: number;
}
// T extends Lengthwise 约束传入的参数必须包含 length 属性
function loggingIdentity<T extends Lengthwise, U>(arg: T, value: U): T {
console.log(arg.length);
return arg;
}泛型接口
ts
interface CreateArrayFunc<T> {
(length: number, value: T): Array<T>
}
let createArray: CreateArrayFunc<any> // 定义泛型的类型
createArray = function <T>(length: number, value: T): Array<T> {
let result: T[] = []
for (let i = 0; i < length; i++) {
result[i] = value
}
return result
}
createArray(3, 'x') // ['x', 'x', 'x']泛型类
ts
class GenericNumber<T> {
zeroValue: T
add: (x: T, y: T) => T
}
// 定义类型 number
let myGenericNumber = new GenericNumber<number>()
myGenericNumber.zeroValue = 0
myGenericNumber.add = function (x, y) {
return x + y
}指定泛型参数的默认类型
ts
// T = string 指定默认类型是 string 当无法推断的时候就会起作用
function createArray<T = string>(length: number, value: T): Array<T> {
let result: T[] = []
for (let i = 0; i < length; i++) {
result[i] = value
}
return result
}声明合并
如果定义了两个相同名字的函数、接口或类,那么它们会合并成一个类型 -> 取并集