├── .eslintignore ├── .vscode ├── settings.json └── launch.json ├── src ├── merge │ ├── interface-merge-a.ts │ ├── module-augmentation-a.ts │ ├── global-augmentation.ts │ ├── module-augmentation-b.ts │ ├── namespace-merge-a.ts │ ├── interface-merge-b.ts │ ├── namespace-merge-b.ts │ └── function-class-interface-merge.ts ├── feat-4.0 │ ├── concat.ts │ └── tail.ts ├── InversifyJS │ ├── index.ts │ ├── inversify.config.ts │ ├── interfaces.ts │ └── entities.ts ├── class-chaining.ts ├── partial-generic.ts ├── merge-functions.ts ├── getter-type-easy.ts ├── select-property │ ├── select-readonly.ts │ ├── select-data-property.ts │ ├── required-keys.ts │ └── select-feature.ts ├── polymorphic-button.tsx ├── repeat-string.ts ├── vue3-ref.ts ├── getter-type-returntype.ts ├── union-to-intersection.ts ├── vue.ts ├── tail.ts ├── getter-type.ts ├── exhaustiveness-checking.ts ├── decorator │ ├── param-decorator.ts │ ├── method-decorator.ts │ └── log-property.ts ├── tree-property.ts ├── create-reversed-map.ts ├── flatten-object.ts ├── function-property-names.ts ├── ioc-loader │ └── good-case.ts ├── vuex.ts ├── type-brand.ts ├── polymorphic-this.ts ├── const-assertions.ts ├── type-vs-interface.ts ├── discriminated-unions-select.ts ├── dispatch-conditional-types-with-builtin-types.ts ├── better-scroll-plugins.ts ├── covariance-contravariance │ └── index.ts ├── feat-4.1 │ └── vuex.ts ├── distribute-conditional-type.ts ├── dispatch-conditional-types.ts ├── reflect-metadata │ ├── server.ts │ └── reflect-metadata.ts ├── mixin-classes.ts ├── connect-leetcode-hire.ts └── state-machine.ts ├── global.d.ts ├── .gitignore ├── index.html ├── tsconfig.json ├── .eslintrc.js ├── package.json └── README.md /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "node_modules/typescript/lib" 3 | } -------------------------------------------------------------------------------- /src/merge/interface-merge-a.ts: -------------------------------------------------------------------------------- 1 | interface Box { 2 | height: number; 3 | width: number; 4 | create(a: string): string; 5 | } 6 | -------------------------------------------------------------------------------- /src/merge/module-augmentation-a.ts: -------------------------------------------------------------------------------- 1 | // observable.ts 2 | export class Observable { 3 | // ... implementation left as an exercise for the reader ... 4 | } 5 | -------------------------------------------------------------------------------- /src/feat-4.0/concat.ts: -------------------------------------------------------------------------------- 1 | type Arr = readonly any[]; 2 | 3 | function concat(arr1: T, arr2: U): [...T, ...U] { 4 | return [...arr1, ...arr2]; 5 | } 6 | 7 | const arr = concat([1] as const, [2, 3] as const); 8 | -------------------------------------------------------------------------------- /src/InversifyJS/index.ts: -------------------------------------------------------------------------------- 1 | import { myContainer } from "./inversify.config"; 2 | import { Warrior } from "./interfaces"; 3 | 4 | const ninja = myContainer.get(Warrior); 5 | 6 | console.log(ninja.fight()); 7 | console.log(ninja.sneak()); 8 | -------------------------------------------------------------------------------- /src/merge/global-augmentation.ts: -------------------------------------------------------------------------------- 1 | // observable.ts 2 | export class Observable { 3 | // ... still no implementation ... 4 | } 5 | 6 | declare global { 7 | interface Array { 8 | toObservable(): Observable; 9 | } 10 | } 11 | 12 | const a = []; 13 | 14 | a.toObservable(); 15 | -------------------------------------------------------------------------------- /src/class-chaining.ts: -------------------------------------------------------------------------------- 1 | class PluginSlide { 2 | next() {} 3 | prev() {} 4 | } 5 | 6 | class BScroll { 7 | scroll() {} 8 | 9 | use(pluginInstance: T): this & T { 10 | return Object.assign(this, pluginInstance); 11 | } 12 | } 13 | 14 | new BScroll().use(new PluginSlide()).next(); 15 | -------------------------------------------------------------------------------- /src/merge/module-augmentation-b.ts: -------------------------------------------------------------------------------- 1 | // map.ts 2 | import { Observable } from "./module-augmentation-a"; 3 | 4 | declare module "./module-augmentation-a" { 5 | interface Observable { 6 | map(f: (x: T) => U): Observable; 7 | } 8 | } 9 | 10 | let ob: Observable; 11 | 12 | ob.map; 13 | -------------------------------------------------------------------------------- /global.d.ts: -------------------------------------------------------------------------------- 1 | // 扩展全局模块 这里可以放一些全局声明 2 | declare global { 3 | type UnionToIntersection = (U extends any 4 | ? (k: U) => void 5 | : never) extends (k: infer I) => void 6 | ? I 7 | : never; 8 | } 9 | 10 | // 注意: 修改"全局声明"必须在模块内部, 所以至少要有 export{}字样 11 | // 不然会报错❌: 全局范围的扩大仅可直接嵌套在外部模块中或环境模块声明中 12 | export {}; 13 | -------------------------------------------------------------------------------- /src/partial-generic.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 如果泛型中有一部分是自动推导的,那么就不能再另外定义手动传入的泛型了。 3 | * 只能暂时用高阶函数的方式把两个泛型拆开,本例中 T 是自动推导得出,E 是手动传入。 4 | */ 5 | function createWithExtra() { 6 | return function(obj: T) { 7 | return obj as T & { 8 | extra: E; 9 | }; 10 | }; 11 | } 12 | 13 | const obj = createWithExtra<{ a: 1 }>()({ foo: "bar" }); 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | ._* 3 | .cache 4 | .project 5 | .settings 6 | .tmproj 7 | *.esproj 8 | *.sublime-project 9 | *.sublime-workspace 10 | nbproject 11 | thumbs.db 12 | *.iml 13 | 14 | # Folders to ignore 15 | .hg 16 | .svn 17 | .CVS 18 | .idea 19 | node_modules/ 20 | jscoverage_lib/ 21 | bower_components/ 22 | dist/ -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/merge/namespace-merge-a.ts: -------------------------------------------------------------------------------- 1 | namespace Animals { 2 | export const haveMuscles = true; 3 | 4 | export class Zebra {} 5 | 6 | export class Dog {} 7 | 8 | export interface Create { 9 | (a: string): number; 10 | } 11 | 12 | // 仅能在当前namespace里使用haveMuscles变量 13 | export function getHaveMuscles() { 14 | return haveMuscles; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/feat-4.0/tail.ts: -------------------------------------------------------------------------------- 1 | function tail(arr: readonly [any, ...T]) { 2 | const [_ignored, ...rest] = arr; 3 | return rest; 4 | } 5 | 6 | const myTuple = [1, 2, 3, 4] as const; 7 | const myArray = ["hello", "world"]; 8 | 9 | // type [2, 3, 4] 10 | const r1 = tail(myTuple); 11 | 12 | // type [2, 3, ...string[]] 13 | const r2 = tail([...myTuple, ...myArray] as const); 14 | -------------------------------------------------------------------------------- /src/merge/interface-merge-b.ts: -------------------------------------------------------------------------------- 1 | // interface合并必须保证某个 key 的类型是独一无二的 2 | // 可以有重复的 key 但类型必须与合并前相同 3 | interface Box { 4 | scale: number; 5 | create(a: number): number; 6 | } 7 | 8 | // 和a文件里的合并了 9 | const box: Box = { 10 | height: 5, 11 | width: 6, 12 | scale: 10, 13 | create(a) { 14 | return a; 15 | } 16 | }; 17 | 18 | // 函数重载合并成功 19 | box.create(2); 20 | box.create("2"); 21 | -------------------------------------------------------------------------------- /src/merge-functions.ts: -------------------------------------------------------------------------------- 1 | let a = () => ({ a: 1, b: 2 }) 2 | let b = () => ({ x: 1, y: 2 }) 3 | let c = () => ({ z: 1, k: 2 }) 4 | 5 | 6 | type TUnionToIntersection = ( 7 |     U extends any ? (k: U) => void : never 8 | ) extends (k: infer I) => void 9 |     ? I 10 |     : never; 11 | 12 | let merge =  any)[]>(...funcs: T): TUnionToIntersection> => { 13 |     return {} as any 14 | } 15 | -------------------------------------------------------------------------------- /src/InversifyJS/inversify.config.ts: -------------------------------------------------------------------------------- 1 | import { Container } from "inversify"; 2 | import { Warrior, Weapon, ThrowableWeapon } from "./interfaces"; 3 | import { Ninja, Katana, Shuriken } from "./entities"; 4 | 5 | const myContainer = new Container(); 6 | myContainer.bind(Warrior).to(Ninja); 7 | myContainer.bind(Weapon).to(Katana); 8 | myContainer.bind(ThrowableWeapon).to(Shuriken); 9 | 10 | export { myContainer }; 11 | -------------------------------------------------------------------------------- /src/InversifyJS/interfaces.ts: -------------------------------------------------------------------------------- 1 | // 定义服务对象标识 2 | export const Warrior = Symbol.for("Warrior"); 3 | export const Weapon = Symbol.for("Weapon"); 4 | export const ThrowableWeapon = Symbol.for("ThrowableWeapon"); 5 | 6 | export interface Warrior { 7 | fight(): string; 8 | sneak(): string; 9 | eat(): void; 10 | } 11 | 12 | export interface Weapon { 13 | hit(): string; 14 | } 15 | 16 | export interface ThrowableWeapon { 17 | throw(): string; 18 | } 19 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Current TS File", 6 | "type": "node", 7 | "request": "launch", 8 | "program": "/Users/shanshihao/.nvm/versions/node/v10.15.3/bin/ts-node", 9 | "args": [ 10 | "${relativeFile}" 11 | ], 12 | "cwd": "${workspaceRoot}", 13 | "protocol": "inspector" 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /src/getter-type-easy.ts: -------------------------------------------------------------------------------- 1 | interface Option { 2 | getters: { 3 | [K in keyof G]: () => G[K]; 4 | }; 5 | } 6 | 7 | type Store = { 8 | [K in keyof G]: G[K]; 9 | }; 10 | 11 | const create = (option: Option): Store => { 12 | return {} as any; 13 | }; 14 | 15 | const store = create({ 16 | getters: { 17 | count() { 18 | return 1 + 1; 19 | } 20 | } 21 | }); 22 | 23 | // number 24 | const count = store.count; 25 | 26 | export { count }; 27 | -------------------------------------------------------------------------------- /src/select-property/select-readonly.ts: -------------------------------------------------------------------------------- 1 | type IfEquals = (() => T extends X ? 1 : 2) extends < 2 | T 3 | >() => T extends Y ? 1 : 2 4 | ? A 5 | : B; 6 | 7 | type ReadonlyKeys = { 8 | [P in keyof T]-?: IfEquals< 9 | { [Q in P]: T[P] }, 10 | { -readonly [Q in P]: T[P] }, 11 | never, 12 | P 13 | >; 14 | }[keyof T]; 15 | 16 | type A = { 17 | readonly a: string; 18 | b: number; 19 | }; 20 | 21 | // "a" 22 | type B = ReadonlyKeys; 23 | -------------------------------------------------------------------------------- /src/polymorphic-button.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | interface ButtonProps { 4 | tagName: "a" | "button"; 5 | } 6 | 7 | function Button

({ 8 | tagName: TagName, 9 | ...props 10 | }: P & UnionToIntersection) { 11 | return ; 12 | } 13 | 14 | const But = ( 15 |