Upgrade to Pro — share decks privately, control downloads, hide ads and more …

TypeScriptでMixinパターンを使う / Mixins Pattern for Ty...

TypeScriptでMixinパターンを使う / Mixins Pattern for Typescript

June 18, 2019 にFukuoka.ts#1 でお話しした「TypeScriptでMixinパターンを使う」の資料です。

linyows

June 18, 2019
Tweet

More Decks by linyows

Other Decks in Programming

Transcript

  1. Fukuoka.ts #1 linyows / Tomohisa Oda GMO Pepabo, Inc.: Principal

    Engineer Blog: https://tomohisaoda.com 2
  2. Fukuoka.ts #1 9 "Mixins first appeared in the Symbolics's object-oriented

    Flavors system (developed by Howard Cannon), which was an approach to object-orientation used in Lisp Machine Lisp. The name was inspired by Steve's Ice Cream Parlor in Somerville, Massachusetts: The owner of the ice cream shop offered a basic flavor of ice cream (vanilla, chocolate, etc.) and blended in a combination of extra items (nuts, cookies, fudge, etc.) and called the item a "mix-in", his own trademarked term at the time."
  3. Fukuoka.ts #1 Exampleʢͦͷલʹʣ 11 ϋϯυϒοΫͷํ͸ྗٕʢJSʣͰmixinΛ࣮ݱ͍ͯͯ͠ɺΠϯλʔϑΣʔεΛࣗલͰॻ͘ํ๏ʹͳ͍ͬͯ· ͢ɻ2017೥ͷTS2.2Ͱ͸Mixinsͷਖ਼ࣜͳܕαϙʔτ͕͞Ε͍ͯΔͷͰɺͦͪΒΛࢀߟʹ͠·͠ΐ͏ɻ class Disposable { isDisposed:

    boolean; dispose() { this.isDisposed = true; } } class Activatable { isActive: boolean; activate() { this.isActive = true; } deactivate() { this.isActive = false; } } class SmartObject implements Disposable, Activatable { constructor() { setInterval(() => console.log(this.isActive + " : " + this.isDisposed), 500); } interact() { this.activate(); } isDisposed: boolean = false; dispose: () => void; isActive: boolean = false; activate: () => void; deactivate: () => void; } applyMixins(SmartObject, [Disposable, Activatable]); let smartObj = new SmartObject(); setTimeout(() => smartObj.interact(), 1000); function applyMixins(derivedCtor: any, baseCtors: any[]) { baseCtors.forEach(baseCtor => { Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => { Object.defineProperty(derivedCtor.prototype, name, Object.getOwnPropertyDescriptor(baseCtor.prototype, name)); }); }); }
  4. Fukuoka.ts #1 12 1 | class Point { 2 |

    constructor(public x: number, public y: number) {} 3 | } 4 | 5 | class Person { 6 | constructor(public name: string) {} 7 | } 8 | 9 | type Constructor<T> = new(...args: any[]) => T; 10 | 11 | function Tagged<T extends Constructor<{}>>(Base: T) { 12 | return class extends Base { 13 | _tag: string; 14 | constructor(...args: any[]) { 15 | super(...args); 16 | this._tag = ""; 17 | } 18 | } 19 | } Example: https://github.com/Microsoft/TypeScript/pull/13743 .JYJO$POTUSVDUPSܕΛఆٛ͢Δ δΣωϦΫε͸.JYJO$POTUSVDUPSͷ੍໿Λड͚Δ
  5. Fukuoka.ts #1 21 | const TaggedPoint = Tagged(Point); 22 |

    23 | let point = new TaggedPoint(10, 20); 24 | point._tag = "hello"; 25 | 26 | class Customer extends Tagged(Person) { 27 | accountBalance: number; 28 | } 29 | 30 | let customer = new Customer("Joe"); 31 | customer._tag = "test"; 32 | customer.accountBalance = 0; 13 Example: https://github.com/Microsoft/TypeScript/pull/13743 function Tagged<typeof Point>(Base: typeof Point): { new (...args: any[]): Tagged<typeof Point>.(Anonymous class); prototype: Tagged<any>.(Anonymous class); } & typeof Point const TaggedPoint: new (x: number, y: number) => Tagged<typeof Point>.(Anonymous class) & Point ͜ͷ··Ͱ͸Anonymous ClassʹͳΔ
  6. Fukuoka.ts #1 14 1 | interface Tagged { 2 |

    _tag: string; 3 | } 4 | 5 | function Tagged<T extends Constructor<{}>>(Base: T): Constructor<Tagged> & T { 6 | return class extends Base { 7 | _tag: string; 8 | constructor(...args: any[]) { 9 | super(...args); 10 | this._tag = ""; 11 | } 12 | } 13 | } 14 | 15 | const TaggedPoint: Constructor<Tagged> & typeof Point = Tagged(Point); 16 | let point: Tagged & Point = new TaggedPoint(10, 20); 17 | point._tag = "hello"; Example: https://github.com/Microsoft/TypeScript/pull/13743 function Tagged<typeof Point>(Base: typeof Point): Constructor<Tagged> & typeof Point const TaggedPoint: new (x: number, y: number) => Tagged & Point ໭ΓͷܕʹλΠϓύϥϝʔλʹఆٛͨ͠Intefaceͱ Intersection TypeΛઃఆ͢Δ͜ͱͰɺܕ҆શʹͳΔ
  7. Fukuoka.ts #1 15 1 | interface Point { 2 |

    x: number; 3 | y: number; 4 | } 5 | 6 | const WithLocation = <T extends Constructor<Point>>(Base: T) => 7 | class extends Base { 8 | getLocation(): [number, number] { 9 | return [this.x, this.y]; 10 | } 11 | } Example: https://github.com/Microsoft/TypeScript/pull/13743 Mixin Class͸ɺλΠϓύϥϝʔλͷ੍໿ͰίϯετϥΫτγάχνϟͷ໭Γ஋ͷܕΛࢦఆ͢Δ͜ͱͰɺ ࠞࡏͤ͞Δ͜ͱ͕Ͱ͖ΔΫϥεͷܕΛ੍ݶͰ͖Δ
  8. Fukuoka.ts #1 ໰୊఺ w ن໛͕େ͖͘ͳΔͱϏϧυ͕࣌ؒ஗͘ͳΔ w ࠶ؼܕ໰୊ w δΣωϦΫεҾ਺໰୊ w

    σίϨʔλʔ໰୊ via: https://www.bryntum.com/blog/the-mixin-pattern-in-typescript-all-you-need-to-know/ by SamuraiJack 16