共计 9890 个字符,预计需要花费 25 分钟才能阅读完成。
TypeScript 介绍
什么是 TypeScript
是 JavaScript 的一个超集,它能够编译成纯 JavaScript。编译进去的 JavaScript 能够运行在任何浏览器上,次要提供了 类型零碎 和对 ES6 的反对。
为什么抉择 TypeScript
- 减少了代码的可维护性
- 包容性强,反对 ES6 语法,
.js
文件间接重命名为.ts
即可 - 兼容第三方库,即便第三方库不是 TypeScript 写的,也能够通过独自编写类型文件供辨认读取
- 社区沉闷,目前三大框架还有越来越多的库都反对 TypeScript 了
TypeScript 只会在编译的时候对类型进行动态查看,如果发现有谬误,编译的时候就会报错。
装置 TypeScript
- 全局装置 TS
npm i -g typescript
- 查看 TypeScript 版本号
tsc -v
我以后版本为 Version 4.0.2
- 初始化生成 tsconfig.json 文件
tsc --init
- 在 tsconfig.json 中设置源代码目录和编译生成 js 文件的目录
"outDir": "./dist",
"rootDir": "./src"
- 监听 ts 文件的变动,每当文件产生扭转就主动编译
tsc -w
之后你写的 ts 文件编译谬误都会间接提醒,如果想运行文件,就到 /dist
目录下找相应的 js 文件,应用 node 运行即可
ts-node 装置
当然这样其实也挺麻烦,咱们想间接运行 TS 文件, 这时能够借助 ts-node
插件
全局装置
npm install -g ts-node
找到文件门路,运行即可
ts-node demo.ts
根底类型
Number 类型
let num: number = 2
Boolean 类型
let isShow: boolean = true
String 类型
let str: string = 'hello'
Array 类型
let arr1: number[] = [1, 2, 3]
let arr2: Array<number> = [2, 3, 4]
Any 类型
let foo: any = 'hello'
foo = 12
foo = false
Null 和 Undefined 类型
null 和 undefined 能够赋值给任意类型的变量
let test1: undefined = undefined
let test2: null = null
let test3: number
let test4: string
test3 = null
test4 = undefined
Void 类型
void 类型像是与 any 类型相同,它示意没有任何类型。当一个函数没有返回值时,其返回值类型是 void
let test5: void = undefined // 申明一个 void 类型的变量没有什么用,因为它的值只能为 undefined 或 null
function testFunc(): void {} // 函数没有返回值
Never 类型
never 类型示意的是那些永不存在的值的类型。
function bar(): never {throw new Error('never reach')
}
Unknown 类型
所有类型都能够赋值给 any,所有类型也都能够赋值给 unknown。
let value: unknown
value = 123
value = 'Hello'
value = true
let value1: unknown = value
let value2: any = value
let value3: boolean = value // Error
let value4: number = value // Error
let value5: string = value // Error
let value6: object = value // Error
let value7: any[] = value // Error
let value8: Function = value // Error
unknown 类型只能被赋值给 any 类型和 unknown 类型自身。
Tuple 类型
数组合并了雷同类型的对象,而元组(Tuple)合并了不同类型的对象。元组示意一个数量和类型都已知的数组
let tupleArr1: [string, number] = ['hello', 10]
// let tupleArr2: [string, number] = [10, 'hello'] // Error
Enum 类型
应用枚举能够定义一些带名字的常量。TypeScript 反对数字的和基于字符串的枚举。
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) // 0 1 2 3
}
函数类型
函数申明
// JS
function func1(x, y) {return x + y}
// TS
function func2(x: number, y: number): number {return x + y}
函数表达式
在 TypeScript 的类型定义中,=>
用来示意函数的定义,右边是输出类型,须要用括号括起来,左边是输入类型。
// JS
let func3 = function (x, y) {return x + y}
// TS 第一种形式
let func4 = function (x: number, y: number): number {return x + y}
// TS 第二种形式
// => 用来示意函数的定义,右边是输出类型,须要用括号括起来,左边是输入类型。let func5: (x: number, y: number) => number = function (x: number, y: number): number {return x + y}
接口定义函数的形态
采纳函数表达式 | 接口定义函数的形式时,对等号左侧进行类型限度,能够保障当前对函数名赋值时保障参数个数、参数类型、返回值类型不变。
interface SearchFunc {(source: string, subString: string): boolean
}
let mySearch: SearchFunc
mySearch = function (source: string, subString: string) {return source.search(subString) !== -1
}
可选参数
对于 TS 的函数,输出多余的(或者少于要求的)参数,是不容许的,那么该怎么设置可选参数呢?,咱们能够在申明之后加一个问号
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)
参数默认值
function getName2(firstName: string = 'monkey', lastName: string) {return `${firstName} ${lastName}`
}
console.log(getName2('jacky', 'Lin'))
console.log(getName2(undefined, 'Lin')) // monkey Lin
void 和 never 类型
当函数没有返回值时,能够用 void 来示意。
当一个函数永远不会返回时,咱们能够申明返回值类型为 never
function func6(): void {// return null}
function func7(): never {throw new Error('never reach')
}
残余参数
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')
函数参数为对象(解构)时
// js 写法
function add({one, two}) {return one + two}
const total = add({one: 1, two: 2})
// ts 写法
function add1({one, two}: {one: number; two: number}): number {return one + two}
const three = add1({one: 1, two: 2})
函数重载
重载容许一个函数承受不同数量或类型的参数时,作出不同的解决。
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 类型断言用来通知编译器你比它更理解这个类型,你晓得你本人在干什么,并且它不应该再收回谬误。
interface Cat {
name: string
run(): void}
interface Fish {
name: string
swim(): void}
// 只能拜访共有的属性
function getName(animal: Cat | Fish): string {return animal.name}
// 将 animal 断言成 Fish 就能够解决拜访 animal.swim 时报错的问题
function isFish(animal: Cat | Fish): boolean {if (typeof (animal as Fish).swim === 'function') {return true}
return false
}
// 任何类型都能够被断言为 any
;(window as any).randomFoo = 1
// 类型断言第一种形式:"尖括号" 语法
let value1: any = 'hello'
let value1Length: number = (<string>value1).length
// 类型断言第二种形式:as
let value2: any = 'world'
let value2Length: number = (value2 as string).length
类
存取器
class Animal {constructor(name: string) {this.name = name}
// getter
get name() {return '名字'}
// setter
set name(value: string) {console.log('setter:' + value)
}
}
let animal = new Animal('monkey')
console.log(animal.name) // monkey
animal.name = 'mk' // setter: mk
拜访修饰符
- public 润饰的属性或办法是私有的,能够在任何中央被拜访到,默认所有的属性和办法都是 public 的
- private 润饰的属性或办法是公有的,不能在申明它的类的内部拜访
- protected 润饰的属性或办法是受爱护的,它和 private 相似,区别是它在子类中也是容许被拜访的
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' // name 值能够拜访且批改
// person1.age // Property 'age' is private and only accessible within class 'Person'.
// person1.sex // Property 'sex' is private and only accessible within class 'Person'.
class Person1 extends Person {constructor(name: string, age: number, sex: string) {super(name, age, sex)
// console.log(this.name, this.age, this.sex) // Property 'age' is private and only accessible within class 'Person'.
}
}
参数属性
同时给类中定义属性的同时赋值
class Animal3 {
public name
constructor(name: string) {this.name = name}
}
console.log(new Animal3('animal3').name) // animal3
class Animal2 {constructor(public name: string) {} // 简洁模式}
console.log(new Animal2('animal2').name) // animal2
- readonly 只读属性
class Animal4 {
readonly name
constructor(name: string) {this.name = name}
}
let animal4 = new Animal4('animal4')
// animal4.name = '5' // Cannot assign to 'name' because it is a read-only property
抽象类
// 抽象类是行为的形象,个别来封装公共属性的办法的,不能被实例化
abstract class CommonAnimal {
name: string
abstract speak(): void}
static
class Animal5 {static sayHi() {console.log('Hello Animal5')
}
}
Animal5.sayHi()
联结类型
联结类型(Union Types)示意取值能够为多种类型中的一种。应用一个 |
宰割符来宰割多种类型
let foo: string | number | boolean
foo = 'test'
foo = 3
foo = true
穿插类型
interface IPerson {
id: string
age: number
}
interface IWorker {companyId: string}
type IStaff = IPerson & IWorker
const staff: IStaff = {id: '007', age: 24, companyId: '1'}
类型别名
type Message = string | string[]
let getMsg = (message: Message) => {return message}
type Weather = 'SPRING' | 'SUMMER' | 'AUTUMN' | 'WINTER'
let weather1: Weather = 'SPRING'
let weather2: Weather = 'AUTUMN'
接口
对象的形态
interface Person1 {
name: string
age: number
}
let person1: Person1 = {
name: 'jacky',
age: 23,
}
形容行为的形象
interface AnimalLike {eat(): void
move(): void}
interface PersonLike extends AnimalLike {speak(): void
}
class Human implements PersonLike {speak() {}
eat() {}
move() {}
}
含构建函数作参数的写法
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')
// let instance2 = createClass(Animal2, 'monkey') // 没有 name 属性则报错
其它任意属性
interface Person2 {
readonly id: number
[propName: string]: any // 任意属性
}
泛型
泛型是指定义函数、接口或类的时候,不预先指定具体的类型,而在应用的时候再指定类型的一种个性;通常用 T 示意,但不是必须应用改字母,只是惯例,通常还有其余罕用字母:
- T(Type):示意一个 TypeScript 类型
- K(Key):示意对象中的键类型
- V(Value):示意对象中的值类型
- E(Element):示意元素类型
泛型类
class GenericNumber<T> {
name: T
add: (x: T, y: T) => T
}
let generic = new GenericNumber<number>()
generic.name = 123
泛型数组
- 写法一
function func<T>(params: T[]) {return params}
func<string>(['1', '2'])
func<number>([1, 2])
- 写法二
function func1<T>(params: Array<T>) {return params}
func1<string>(['1', '2'])
func1<number>([1, 2])
泛型接口
能够用来束缚函数
interface Cart<T> {list: T[]
}
let cart: Cart<number> = {list: [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,这时咱们应该尽量应用接口代替类型别名
- 当咱们须要应用联结类型或者元组类型的时候,类型别名会更适合
多个泛型
// 不借助两头变量替换两个变量的值
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)
默认泛型
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)
泛型束缚(继承)
interface WithLength {length: number}
// extends 来继承
function logger<T extends WithLength>(val: T) {console.log(val.length)
}
logger('hello')
logger([1, 2, 3])
// logger(true) // error 没有 length 属性
泛型工具类型
为了不便开发者 TypeScript 内置了一些罕用的工具类型,比方 Partial、Required、Readonly、Pick 等。它们都是应用 keyof 实现。
keyof 操作符能够用来一个对象中的所有 key 值。
interface Person1 {
name: string
age: number
sex?: string
}
type PersonKey = keyof Person1 // 限度 key 值的取值
function getValueByKey(p: Person1, key: PersonKey) {return p[key]
}
内置的工具类型
// type Partial<T> = {[P in keyof T]?: T[P]}
type PersonSearch = Partial<Person> // 全副变可选
// type Required<T> = {[P in keyof T]-?: T[P] }
type PersonRequired = Required<Person> // 全副变必选
// type ReadOnly<T> = {readonly [P in keyof T]: T[P] }
type PersonReadOnly = Readonly<Person> // 全副变只读
// type Pick<T, K extends keyof T> = {[P in K]: T[P]}
type PersonSub = Pick<Person, 'name'> // 通过从 Type 中抉择属性 Keys 的汇合来构造类型。
类数组
let root = document.getElementById('root')
let children: HTMLCollection = root!.children // ! 代表非空断言操作符
let childNodes: NodeListOf<ChildNode> = root!.childNodes
类型爱护
更明确的判断某个分支作用域中的类型,次要尝试检测属性、办法或原型,以确定如何解决值。
typeof
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
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
class Student {
name: string
play: string[]}
class Teacher {
name: string
teach: string
}
type SchoolRole = Student | Teacher
function getRoleInformation(role: SchoolRole) {if ('play' in role) {console.log(role.play)
}
if ('teach' in role) {console.log(role.teach)
}
}
自定义类型爱护
比方有时两个类型有不同的取值,也没有其余能够辨别的属性
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