在TS中,unique symbol 生成了一个在所有代码中保持唯一的名称。用户声明的名称绝对不会匹配生成的名称,使用该名称创建一个属性,需要给这个属性定义一个类型,但是我们不关心它的实际值,只是为了区分类型,所以选择了最适合的单元类型void。所以当要创建 NS和Lbfs的Subtype(亚类型、儿类型),只能显示地继承。
Subtype(亚类型、儿类型)的极端情况
把任何类型赋值给它的类型(Assigning anything to):any, unknown
给任何东西赋值类型(assigning to anything):any- 从而绕过类型检查
反序列化any
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
// JSON.parse()反序列化返回类型是 any class User { name: string; constructor(name: string) { this.name = name; } } // 只是封装了 JSON.parse() 并返回了 any 类型的一个值 functiondeserialize(input: string): any{ returnJSON.parse(input) } functiongreet(user: User): void{ console.log(`hi ${user.name}`) } // 反序列化一个有效的 User JSON greet(deserialize('{"name": "Alice"}')) // 也可以反序列化一个不是 User 对象的对象 greet('{}') // 输出hi undefined,因为 `any`会绕过类型检查
class Shape { // Shape members } declareconst TriangleType: unique symbol; class Triangle extends Shape { [TriangleType]: void; // 判断给定的实例是否描述了一个直角三角形 isRightAbgled(): boolean { let result: boolean = false; // determine whether it is a right-angled triangle return result } // more triangle members }
class CEO { isBusy(): boolean {} answer(question: string): string {} } class Department {} class Budget {} class Company { private ceo: CEO = new CEO(); private departments: Department[] = [] private budget: Budget = new Budget() askCEO(question: string): string | undefined { if (!this.ceo.isBusy()) { returnthis.ceo.answer(question) } } }
functionextend<First, Second>(first: First, second: Second): First & Second{ const result: unknown = {} // 首先,迭代第一个对象的所有成员,把他们复制到result中 for (const prop in first) { if (first.hasOwnProperty(prop)) { (<First>result)[prop] = first[prop] } } // 接下来,对第二个类型的成员做相同的处理 for(const prop in second) { if (second.hasOwnProperty(prop)) { (<Second>result)[prop] = second[prop] } } return <First & Second>result }
混入行为
1 2 3 4 5 6 7 8 9 10 11 12 13
// 不定义 Cat, 而是定义 喵喵行为 的宠物类型 // 豹子,猫 都可以喵喵 // 由于没有扩展捕猎行为,所以还不是 Cat class MeowingPet extends Pet { meow(): void {} } class HunterBehavior { track(): void {}; stalk(): void {}; pounce(): void {}; } type Cat = MeowingPet & HunterBehavior const fluffy: Cat = extend(new MeowingPet(), new HunterBehavior())
习题
跟踪快递单和包裹的状态
1 2 3 4 5 6 7 8 9 10 11 12 13
class Letter {} class Package {} class Tracking { private status: string; constructot(status: string) { this.status = status } updateStatus(status) { this.status = status } } type TrackingLetter = Letter & Tracking type TrackingTracking = Package & Tracking
functionprint<T>(iterator: Iterator<T>): void{ let result: IteratorResult<T> = iterator.next() while(!result.done) { console.log(result.value) result = iterator.next() } }
functioncontains<T>(value: T, iterator: Iterator<T>): boolean{ let result: IteratorResult<T> = iterator.next() while(!result.done) { if (result.value === value) returntrue result = iterator.next() } }
'使用 Iterable 实参的print()和contains()'
1 2 3 4 5 6 7 8 9 10 11 12 13
functionprint<T>(iterable: Iterable<T>): void{ for (const item of iterable) { console.log(item) } }
functioncontains<T>(value: T, iterable: Iterable<T>): boolean{ for (const item of iterable) { // return 可以退出 for...of 循环 if (item === value) returntrue } returnfalse }
使用生成器的二叉树迭代器
1 2 3 4 5 6 7 8 9 10 11 12 13
function* inOrderIterator<T>(root: BinaryTreeNode<T>):IterableIterator<T> { if (root.left) { for (const value of inOrderIterator(root.left)) { yield value; } }; yield root.value if (root.right) { for (const value of inOrderIterator(root.right)) { yield value; } } }
function* square(iter: Iterable<number>): IterableIterator<number> { for (const value of iter) { yield value ** 2 } } function* take<T>(iter: Iterable<T>, n: number): IterableIterator<T> { for (const value of iter) { if (n-- <= 0) return yield value } } const values: IterableIterator<number> = take(square(generateRandomNumbers()), 5) for (const value of values) { console.log(value) } // 练习 实现 drop 丢弃一个迭代器的前n个元素而返回其余元素 function* drop<T>(iter: Iterable<T>, n: number): IterableIterator<T> { for (const [v, k] of iter) { if (n-- > 0) continue yield value } } function* count(): IterableIterator<number> { let n: number = 0 while(true) { n++ yield n } } for (const value of take(drop(count(), 5), 5)) { console.log(value) }
functionClamp<TextendsIcomparable<T>>(v: T, min: T, max: T): T | undefined{ if (!value) returnundefined if (value.compareTo(min) === ComparisonResult.LessThan) return min if (value.compareTo(max) === ComparisonResult.GreeterThan) return max return value }
// 将 tagged union 类型转为 union type interface Example { a: string; b: number; } type SingleProp<T, K extends keyof T> = {[P in keyof T]?: P extends K ? T[P] : never} type oneOf<T> = {[K in keyof T]: Pick<T, K> & SingleProp<T, K>}[keyof T] type ExamplePropUnion = oneOf<Example>
几何形状集合:添加一个 kind 属性标签,方便使用时判断或者强制转换形状
1 2 3 4 5 6 7 8 9 10 11 12
class Point { readonly kind: string = 'Point' x: number = 0; y: number = 0; } class Circle { readonly kind: string = 'Circle' x: number = 0; y: number = 0; radius: number = 0 } type Shape = Point | Circle
declareconst percentageType: unique symbol; class Percentage { readonly value: number; [percentageType]: void; // private 声明构造函数,只有工厂函数才能调用 privateconstructor(value: number) { this.value = value } static makePercentage(value: number): Percentage | undefined { if (value < 0) value = 0; if (value > 100) value = 100; returnnew Percentage(value) } }
添加类型信息
类型转换
1 2 3 4 5 6 7 8 9 10 11 12 13 14
// 类型强制转换导致运行时错误 class Bike { ride(): void {} } class SportsCar { drive(): void {} } let myBike: Bike = new Bike() Bike.ride() // 先把 myBike 转成 unknown 再转成 SportsCar let myPretendSportsCar: SportsCar = <SportsCar><unknown>myBike; // 或者写成: // let myPretendSportsCar: SportsCar = myBike as unknown as SportsCar; myPretendSportsCar.drive() // 运行时错误
class Bike {} class Car {} // 假设创建 Car 开销较大 functionchooseMyRide(bike: Bike, car: Car) Car | Bike{ if (isItRaining()) { return car } else { return bike } } // 调用时就创建Car chososeMyRide(new Bike(), new Car())
延迟生成 car
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
class Bike {} class Car {} // 假设创建 Car 开销较大 // 参数改为一个返回 Car 的函数 functionchooseMyRide(bike: Bike, car: () => Car) Car | Bike{ if (isItRaining()) { return car() } else { return bike } } functionmakeCar(): Car{ returnnew Car() } // 调用时就创建Car chososeMyRide(new Bike(), makeCar)
lambda 匿名函数
1 2 3 4 5 6 7 8 9 10 11 12 13
// 使用匿名函数 class Bike {} class Car {} // 假设创建 Car 开销较大 // 参数改为一个返回 Car 的函数 functionchooseMyRide(bike: Bike, car: () => Car) Car | Bike{ if (isItRaining()) { return car() } else { return bike } } // 调用时就创建Car chososeMyRide(new Bike(), () =>new Car())
使用 map,filter,reduce
map()
对每个值调用一个函数,返回结果集合
1 2 3 4 5 6 7 8
// 自制 map() functionmap<T, U>(items: T[], func: (item: T) => U) : U[] { let result: U[] = [] for (const item of items) { result.push(func(item)) } return result }
filter()
丢弃某些元素,接受一个实参并返回一个 boolean 的函数也称为 谓词
1 2 3 4 5 6 7 8 9 10
// 自制 filter() functionfilter<T>(items: T[], pred: (item: T) => boolean): T[] { let result: T[] = [] for (const item of items) { if (pred(item)) { result.push(item) } } return result }
reduce()
将所有集合项合并为一个值
1 2 3 4 5 6 7 8
// 自制 reduce() functionreduce<T>(items: T[], op: (x: T, y: T) => T, init: T):T{ let result: T = init for (const item of items) { result = op(result, item) } return result }
幺半群
抽象代数处理集合以及集合上的操作。 如果类型 T 的一个操作接受 2个 T 作为实参,并返回另一个 T, 即 (T, T) => T,则可以把该操作解释为 T 值集合上的一个操作。例如:number 集合 +,即 (x, y) => x + y, 就构成了一个代数结构。 相关性:表明对元素序列应用操作的顺序并不重要,因为最终结果都是相同的。
// 使用 闭包创建函数 返回斐波那契数列中的下一个数字 functionfib1(): () => number{ let a: number = 0; let b: number = 0; return() => { let next: number = a a = b b = b + next return next } } // 使用生成器创建函数 返回斐波那契数列中的下一个数字 function* fib2(): IterableIterator<number> { let a: number = 0; let b: number = 0; while(true) { let next: number = a a = b b = b + next return next } }
异步执行的运行时间长的操作
同步执行
1 2 3 4 5 6 7 8 9 10 11
functiongreet(): void{ const readlineSync = require('readline-sync'); let name: string = readlineSync.question('what is your name') console.log(`Hi ${name}`) } functionweather(): void{ const open = require('open') open('heeps://weather.com/') } greet(); // 先调用 greet weather(); // 等待 greet 执行完毕
$ ghci> True && False False $ ghci> True && True True $ ghci> False || True True $ ghci> not False True $ ghci> not (True && True) False $ ghci> 5 == 5 True $ ghci> 1 == 0 False $ ghci> 5 /= 5 False $ ghci> 5 /= 4 True # but we can do 5 + 4.0 $ ghci> 5 + 4.0 # 5 can act like an integer or a floating-point number. 9.0
function: prefix, infix
prefix function
prefix function like succ function takes anything that has a defined successor and return that successor. it means it can increase the parameter(like numbers). also, The functions min and max take two things that can be put in an order(like numbers).
ghci> succ 8 9 # min returns the one that's lesser ghci> min 9 10 9 ghci> min 3.4 3.2 3.2 # max return the one that's greater ghci> max 100 101 101 # Function applications have the highest precedence, as reflected by the fact that when mixed with other operators # What that means for us is that these two statements are equivalent. ghci> succ 9 + max 5 4 + 1 16 ghci> (succ 9) + (max 5 4) + 1 16 # if we wanted to get the successor of the product of numbers 9 and 10, we couldn't write succ 9 * 10 ghci> succ 9 * 10 100 # It is equivalent to ghci> (succ 9) * 10 100 # we have to write this statement to get 91 ghci> succ (9 * 10) 91
infix function
backticks: `
1 2 3 4 5 6 7 8
# division # there maybe some confusion as to which number is doing the division and which one is being divided. ghci> div 92 10 9 # we can call it as an infix function by surrounding it with backticks. # it's much clearer ghci> 92 `div` 10 9