后续//
概述
编译器
过程
- Typescirpt源码 -> TS AST
- 类型检查器检查AST
- TS AST->js源码
- js源码->js AST
- AST->字节码
-
工具
TSC 操作上面前三个步骤
后三步由浏览器 NodeJS 或其他的js引擎中的js运行时操作
类型系统
分类
显示声明
-
TS VS JS
类型绑定
- js动态,运行时才知道类型
- ts是静态,在编译前能捕获部分错误
- 是否自动类型转换
- js 会
- ts 多数不会
- 何时检查类型
- js不在乎使用类型,会尽所能把你提供类型转换成预期类型
- ts在编译时做类型检查,会做静态分析
何时报告错误
tsc
- tslint
-
tsconfig.json
-
tslint.json
-
index.ts
编译
运行
类型全解
any
unknown的子类型
- 如果想用any,最好使用显示注解,
noImplicitAny 和strict启用都可针对隐式any类型报错
unknown
必须显示注解
- 可以比较
不会推导unknown类型为某种特定类型必须先证明一个值是某种类型
let a: unknown = 30
if(typeof a === 'number'){
let d = a+10
}
类型字面量
-
const
常量
const c = true //这里推导出的c就是 一个true类型“类型字面量”
boolean
any的子类
-
number
-
bigint
新引入
- 处理较大整数时不用担心舍入误差
-
string
symbol
新建一个符号,不与其他任何符号相等,即使用相同名称创建一个也是这样
unique symbol 一定是const的,不能用let
对象
结构化类型
一种编程设计风格,只关心对象有哪些属性,不管属性使用什么名称,鸭子类型
对象字面量句法
let a:{b:number} = {
b: 12
}
属性可选
let a:{
b:number
c?:string
[key: number]: boolean // 任意多个数字属性
}
//a={b:1,10:true}
属性只读
-
声明对象的四种方式
字面量表示法
- 空对象字面量表示
- object类型
-
类型别名,并集,交集
别名
type Age = number
type Person = {
name: string
age: Age
}
交集并集
type Cat = {name:string,purrs:boolean}
type Dog = {name:string,barks:boolean,wags:boolean}
type CatOrDog = Cat | Dog
type CatAndDog = Cat & Dog
数组,元组,只读数组
数组 Array
function buildArray(){
let a = []
a.push(1)
a.push('x')
return a
}
let myArray = buildArray()
myArray.push(true) //这里会报错,当数组离开定义时所在的作用域后确定类型不在扩张
元组 Tupe
数组子类型,定义数组的一种特殊方式
- 声明时必须显示注解类型
- 长度固定,各索引位上的值具有固定已知类型
- 所以比数组安全 ```typescript let trainFares:[numbers,number?][]=[ [3.75], [8.23,7.7] ]
let list:[number,boolean, …string[]] = [1,false,’a’,’b’]
<a name="QOQBJ"></a>
#### 只读数组
```typescript
type A = readonly string[]
type B = ReadonlyArray<string>
type C = Readonly<string[]>
null undefined,void never
- null是缺少值
- undefined是尚未赋值的变量
- void 是函数没有显示返回任何值
- never是函数不返回(抛出异常,永远运行下去)
- unknown是其他每个类型的父类型,never是其他每个类的子类型
枚举
const enum Language{
English,
Spanish,
Russian
}
//
- 枚举不安全,
- 如果坚持使用,可以制定一些TSLint规则,如发现数值枚举,和非const枚举发出提醒
函数
声明函数和调用函数
5种函数什么方式
```typescript //具名函数 function gree(name: string){ return ‘hello ‘+ name } //函数表达式 let greet2 = function(name: string){ return ‘hello ‘+ name
} //箭头函数表达式 let greet3 =(name: string) => { return ‘hello ‘+ name } //箭头函数简写 let greet4 =(name: string) => return ‘hello ‘+ name //函数构造法
let greet5 = new Function(‘name’,’return “hello “ + name’)
<a name="Dw11W"></a>
#### 可选参数和默认参数
```typescript
function log(message:string,userId?: string){
//...
}
function log(message:string,userId='not signed in'){
//...
}
剩余参数
- 数组实现
- arguments实现//不安全 类型是any
-
函数的其他调用方式
call
- apply
-
注解this类型
this的值取决于调用方式,
- 可以指定this ,防止这种混淆 ```typescript function fancyDate(this:Date){ return ${this.getDate()}/${this.getMonth()}/${this.getFullYear()} }
<a name="n7xbx"></a>
#### 生成器函数
- 生成一系列值的便利方式
- 惰性的
```typescript
function* createFibonaciiGenerator(){
let a = 0
let b = 1
while(true){
yield a; //调用next 把这个给调用方,然后停止执行
[a,b] = [b,a+b]
}
}
// 显示注解生成器
function* createNumbers(): IterableIterator<number>{
//....
}
迭代器
- 可迭代对象
- 有Symbol.iterator的属性对象,该属性的值为一个函数,返回迭代器
- 迭代器
- 有next方法的对象,该方法返回一个具有 value 和done属性的对象 ```typescript let numbers = { *Symbol.iterator{ for(let n=1;n<10;n++){ yield n } } }
// 使用 for-of 迭代一个迭代器 for(let a of numbers){ //1,2,3 etc } //展开一个迭代器 let allNumbers = [… numbers] //析构 一个迭代器 let [one,two, …rest] = numbers
<a name="tO5Ce"></a>
#### 调用签名
- 表示 函数类型的句法,也称调用签名
- 无法表示默认值
```typescript
(a:number,b:number) => number
type Log = (message: string, userId?: string) => void
let log: Log = (
message,
userId = 'not signed in'
) => {
let time = new Date().toISOString()
console.log(time,message,userId)
}
上下文类型推导
- 行内声明,无需显式注解函数类型 ```velocity function times( f:(index: number)=>void, n:number ){…}
times(n=>console.log(n),4) //可以
function f(n){…} times(f,4) //不可以
<a name="onlSm"></a>
#### 函数类型重载
- 使用完整型调用签名,声明多个
- 自己动手组合多个签名实现声明的签名
```javascript
type Reserve = {
(from:Date,to:Date, destination: string):Reservation
(from:Date,destination:String):Reservation
}
let reserve: Reserve=(
from:Date,
toOrDestination:Date|string,
destination?:string
) => {
//...
if(toOrDestination instanceof Date&&destination !== undefined){
//...
}else if(typeof toOrdestination === 'string'){
}
}
多态
泛型参数
type Filter = {
<T>(array:T[],f:(item: T)) => boolean):T[]
}
let filter:Filter = (array,f) => //...
type Filter<T> = {
(array:T[],f:(item: T)) => boolean):T[]
}
let filter:Filter<number> = (array,f) => //...
type Filter = <T>(array:T[],f:(item: T)) => boolean) => T[]
let filter:Filter = //...
type Filter<T> = (array:T[],f:(item: T)) => boolean) => T[]
let filter:Filter = //...
fuction filter<T>(array:T[],f:(item: T)) => boolean):T[]{...
}
自动推导与显示注解
泛型别名
type MyEvent<T> = {
target: T
type: string
}
function triggerEvent<T>(event: MyEvent<T>):void{
//...
}
triggerEvent({//T是 Element|null
target:document.querySelector('#myButton'),
type:'mouseover'
})
受限的多态
function mapNode<T extends TreeNode>(
node: T,
f:(value: string)=>string
):T{
return{
...node,
value:f(node.value)
}
}
/// 多个约束
type HasSides = {numberOfSides: number}
type SidesHaveLength = {sideLength: number}
function logPerimeter<Shape entends HasSides & SidesHaveLength>(s: Shape){
console.log(s.numberOfSides*s.sideLength)
return s
}
// 模拟变长参数
function call<T extends unknown[],R>(
f:(...args:T)=>R,
...args:T
):R{
return f(...args)
}
泛型默认类型
type MyEvent<T = HTMLELement> = {
target: T
type: string
}
类型驱动开发
-
类和接口
类和继承
class extends
- abstract
- private protected public
- readonly 属性
```typescript
class Postion{
constructor(
private file: File, //自动赋值给 this.file 并设置为私有
private rank: Rank
){} } abstract class Piece{ //不能new protected position: Position constructor( private readonly color:Color, //自动赋值,并设为私有 file:File, rank:Rank ){ this.position = new Position(file,rank) } abstract canMoveTo(position: Position):boolean //子类必须实现
}
class King extends Piece{ canMoveTo(postion:Position){ //… } }
<a name="PwY0d"></a>
#### super
- 方法调用 super.take
- 构造方法调用 super()
- 只能访问父方法,不能访问父属性
<a name="Rq8Bi"></a>
### 以this为返回类型
```typescript
let set = new Set
set.add(1).add(2)
class Set{
add(value:number):Set{ //这里的Set 改为 this,下面子类的实现就可以删除
//...
}
}
class MutableSet extends Set{
add(value:number):MutableSet{
//...
}
}
接口
- 类经常当做接口使用
- 与类型别名相似,接口是一种命名类型的方式 ```typescript type Food = { calories: number tasty:boolean } type Sushi = Food&{ salty: boolean }
interface Food { calories: number tasty:boolean }
interface Sushi extends Food{ salty: boolean } ```
与别名的区别
- 别名更为通用,右边可以是任何类型,包括类型表达式
- 扩展接口时,会检查扩展接口是不是可以赋值给被扩展的接口
-
声明合并
-
实现
implements
可以声明属性,不能使用可见性修饰符,不能用static,可以用readonly
实现接口还是扩展抽象类
类是结构化类型
两个不用名称的类,实现了同样的方法或有同样的属性,可以替换使用
-
类既声明值也声明类型
多态
混入
装饰器
-
模拟final类
-
类型进阶