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

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

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

5d769d109697012317c09c6a27a6a4bf?s=128

linyows

June 18, 2019
Tweet

Transcript

  1. Fukuoka.ts #1 TS TypeScriptͰMixin PatternΛ࢖͏ Tomohisa Oda — GMO Pepabo,

    Inc. / June 18th 2019
  2. Fukuoka.ts #1 linyows / Tomohisa Oda GMO Pepabo, Inc.: Principal

    Engineer Blog: https://tomohisaoda.com 2
  3. Fukuoka.ts #1 Fukuoka.go Organizer 3 https://fukuokago.dev/ ͜ͷՆʢʣʹFGNͰ Go ConferenceΛ෱ԬͰ։࠵͠·͢ https://fukuoka.gocon.jp/

    ͜ͷαΠτ͸ɺNuxt.jsͱ TypeScriptͰ࡞͍ͬͯͯɺGitHub ͷPublic RepositoryͰ͢
  4. Fukuoka.ts #1 Mixinύλʔϯ࢖͍ͬͯ·͔͢ʁMixinͬͯΘ͔Γ·͢ΑͶʁ 4

  5. Fukuoka.ts #1 lMixin͸ɺProgrammer͕ClassʹίʔυΛૠೖ͢Δ͜ͱΛՄೳʹ͢Δ ݴޠͷ֓೦Ͱ͢ɻMixin Programming͸ɺιϑτ΢ΣΞ։ൃͷελΠϧ Ͱɺػೳͷ୯Ґ͸Class಺Ͱ࡞੒͞Ε͔ͯΒଞͷClassͱMix͞Ε·͢ɻz h t t p

    s : / / e n . w i k i p e d i a . o r g / w i k i / M i x i n # D e f i n i t i o n 5
  6. Fukuoka.ts #1 Mixin 6 Inheritence ༗޲ඇ८ճάϥϑ ໦ߏ଄

  7. Fukuoka.ts #1 ͭ·ΓɺػೳʢৼΔ෣͍ʣΛ੾Γग़ͯͦ͠ͷৼΔ෣͍Λ͋ΔClassʹऔΓࠐΉɻ ·ͨɺܧঝͷΑ͏ͳɺߏ଄Խ͞Εͨ਌ࢠؔ܎Ͱ͸දݱͰ͖ͳ͍ݱ࣮ੈքͷෳࡶੑΛิ͏ɻ 7

  8. Fukuoka.ts #1 Mixins͸Կ͕خ͍͠ͷ͔ w ৼΔ෣͍ʹ໊લΛ͚ͭͯ෼ׂͰ͖Δ w ૊Έ߹Θͤͯ࢖͏ͷͰίʔυͷ࠶ར༻ੑ͕ߴ͍ w খ͘͞෼ׂՄೳͳͷͰ؅ཧ͠΍͍͢ w

    ݺͼग़͠ଆʹػೳͷબ୒͕Մೳ 8 P I G E O N N O I S Y C AT M O VA B L E F LYA B L E
  9. 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."
  10. Fukuoka.ts #1 Usecase w ༷ʑͳཁٻʹରԠ͢ΔϏδωεϩδοΫ w େ͖Ίͷ"1*ΫϥΠΞϯτ ྫHJUIVCDPNQFQBCPHNPQH 10

  11. 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)); }); }); }
  12. 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ͷ੍໿Λड͚Δ
  13. 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ʹͳΔ
  14. 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Λઃఆ͢Δ͜ͱͰɺܕ҆શʹͳΔ
  15. 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͸ɺλΠϓύϥϝʔλͷ੍໿ͰίϯετϥΫτγάχνϟͷ໭Γ஋ͷܕΛࢦఆ͢Δ͜ͱͰɺ ࠞࡏͤ͞Δ͜ͱ͕Ͱ͖ΔΫϥεͷܕΛ੍ݶͰ͖Δ
  16. 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
  17. Fukuoka.ts #1 ·ͱΊ w ΦϒδΣΫτࢦ޲ϓϩάϥϛϯάʹ͓͚ΔMixinͷఆٛΛ͓͞Β͍͠·ͨ͠ w TypeScriptͰ͸ݴޠ࢓༷ͱͯ͠MixinΛ؆୯ʹ࢖͑ΔΠϯλʔϑΣʔεΛ࣋ͬͯ ͍ΔΘ͚Ͱ͸͋Γ·ͤΜ͕ɺMixinύλʔϯ͕Ͱ͖Δ͜ͱ͕Θ͔Γ·ͨ͠ w ·ͨɺܕ҆શͰ͋ͬͨΓɺػೳΛબ୒ͯ͠ఏڙͨ͠Γɺ։ൃͷதͰ༗༻ͦ͏Ͱ͋

    Δ͜ͱ͕Θ͔Γ·ͨ͠ w ࢖͍ॴΛΘ͖·͑ɺTypeScriptΛ͔ͭͬͨ։ൃઃܭͷҾ͖ग़͠ͷ̍ͭʹͲ͏ͧ 17