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

TypeScriptでType Match的なことをする話 #すえなみチャンス暑気払い

kyo_ago
August 04, 2019

TypeScriptでType Match的なことをする話 #すえなみチャンス暑気払い

kyo_ago

August 04, 2019
Tweet

More Decks by kyo_ago

Other Decks in Programming

Transcript

  1. // ਏ͍ྫ class Message { constructor( private sending: boolean, private

    editing: boolean, private deleted: boolean, ) {} canEdit(): boolean { if ( !this.sending && !this.deleted ) { return false; } return true; } }
  2. // ΋ͬͱਏ͍ྫ class Message { constructor( private sending: boolean, private

    loading: boolean, // New! private editing: boolean, private deleted: boolean, ) {} canEdit(): boolean { if ( !this.sending && !this.loading && // New! !this.deleted ) { return false; } return true; } }
  3. // ਏ͍ྫ class Message { constructor( private sending: boolean, private

    loading: boolean, // New! private editing: boolean, private deleted: boolean, ) {} canEdit(): boolean { if ( !this.sending && !this.loading && // New! !this.deleted ) { return false; } return true; } }
  4. // ֎෦͔Βݟͯಉ͡ঢ়ଶ͔ʁ const message = new Message(/.../); const getEditableMessage =

    () => { return message.canEdit() ? "मਖ਼Ͱ͖ΔΑʂ" : "मਖ਼ Ͱ͖ͳ͍Αʂ"; } const showEditableIcon = () => { return message.canEdit(); }
  5. // ָͳྫ type MessageStateLiterals = "sending" | "loading" | "editing"

    | "deleted"; class MessageState { private state: MessageStateLiterals; constructor( sending: boolean, loading: boolean, editing: boolean, deleted: boolean, ) { // ৚݅ʹԠͯ͡this.state΁MessageStateLiteralsͷ͍ͣΕ ͔Λ୅ೖ } match<R>(matcher: { [key in MessageStateLiterals]: () => R }): R { return matcher[this.status](); } }
  6. class Message { private state: MessageState; constructor( private sending: boolean,

    private loading: boolean, private editing: boolean, private deleted: boolean, ) { this.state = new MessageState(true, false, false, false); } canEdit(): boolean { return this.state.match({ "sending": () => false, "loading": () => false, "editing": () => true, "deleted": () => false, }); } }
  7. ϩδοΫ͕ू໿͞ΕΔ // ਏ͍ྫ class HogeEntity { private state: HogeState; constructor(

    private id: HogeId, private name: string, private enable: boolean, private body: string, private selected: boolean, private focused: boolean, ) {} toHoge() { // ঢ়ଶͷมߋ͕಺෦ʹӅṭ͞Ε͓ͯΓɺςετ͕೉͍͠ } }
  8. ϩδοΫ͕ू໿͞ΕΔ // ָͳྫ type MessageStateLiterals = "sending" | "loading" /…/;

    class MessageState { private state: MessageStateLiterals; constructor( sending: boolean, loading: boolean, editing: boolean, deleted: boolean, ) { // ৚݅ʹԠͯ͡this.state΁MessageStateLiteralsͷ͍ͣΕ͔Λ୅ೖ } match<R>(matcher: { [key in MessageStateLiterals]: () => R }): R { return matcher[this.status](); } }
  9. ϩδοΫ͕ू໿͞ΕΔ // ָͳྫʢςετʣ describe(`MessageState`, () => { const matcher =

    { "sending": () => "sending", // ... }; [ { enable: false, selected: false, focused: false, result: "disabled" }, // શͯͷม਺ͷ૊Έ߹ΘͤΛॻ͘ ].forEach(param => { it(JSON.stringify(param), () => { let state = new MessageState(param.enable, param.selected, param.focused); assert(state.match(matcher) === param.result); }); }); });
  10. ঢ়ଶͷ௥Ճ࣌ʹطଘͷ࣮૷ʹରͯ͠ ໢ཏੑΛڧ੍Ͱ͖Δ // key͕ෆ଍͍ͯ͠Δ৔߹ɺίϯύΠϧ͕௨Βͳ͍ɻ type MessageStateLiterals = "sending" | "loading"

    // …; class MessageState { // ... match<R>(matcher: { [key in MessageStateLiterals]: () => R }): R { return matcher[this.status](); } } const state = new MessageState(/.../); state.match({ "sending": () => true, // Compile error! });
  11. ঢ়ଶʹର໊ͯ͠લ͕ͭ͘ // ਏ͍ྫ class Message { constructor( private sending: boolean,

    private editing: boolean, private deleted: boolean, ) {} canEdit(): boolean { // canEdit͸ʮฤू͕Մೳʯͱ͍͏݁ՌΛฦ͍ͯ͠Δ͚ͩͰɺ // ͦͷ࣌ͷMessageͷঢ়ଶ͸ද͍ͯ͠ͳ͍ɻ } }
  12. ҎԼͷΑ͏ͳDMBTTΛఆٛ͢Δ͜ͱ Ͱ࣮૷Λ؆ུԽͰ͖Δ type BaseTypeLiterals<L extends string, R> = { [key

    in L]: () => R } abstract class BaseType<TypeLiterals extends string> { constructor(protected value: TypeLiterals) {} getValue(): TypeLiterals { return this.value; } equals(type: TypeLiterals): boolean { return this.getValue() === type; } equalType<T extends this>(target: T): boolean { return this.getValue() === target.getValue(); } match<R>(matcher: BaseTypeLiterals<TypeLiterals, R>): R { return matcher[this.getValue()](); } }
  13. ҎԼͷΑ͏ͳDMBTTΛఆٛ͢Δ͜ͱ Ͱ࣮૷Λ؆ུԽͰ͖Δ class MessageState extends BaseType<"sending" | /.../ > {

    constructor( sending: boolean, loading: boolean, /.../ ) { if (sending) { return super("sending"); } // ඞཁͳ෼ذΛ௥Ճ } }
  14. 2"

  15. 2" w 2ɿ࢓༷ύλʔϯʢ4QFDJpDBUJPOύλʔϯʣͰ͸ʁ w "ɿ:FTɻͨͩɺ5ZQF4DSJQUͰTFBMFEम০ࢠΛ࠶ݱͰ͖ͨͷ΋ίʔυΛॻ্͘Ͱ͸ศ རͩͬͨ
 ʢ4DBMB࢖ͬͯΔਓʹ͸౰ͨΓલͰ΋ʣ w 2ɿ+BWBͷ&OVNͰ͸ʁ w

    "ɿ+BWBͷ&OVNͰ͸TXJDIͷ໢ཏੑΛڧ੍Ͱ͖ͳ͍ͷͰे෼Ͱ͸ͳ͍ͱ͍͏ཧղ
 ʢͪͳΈʹ5ZQF4DSJQUͷFOVN͸શ͘໾ʹཱͨͳ͍ͱ͍͏ͷ΋ਃ͠ఴ͓͖͑ͯ·͢ʣ w 2ɿ4XJGUͷ&OVNͰ͸ʁ w "ɿ:FTɻ<4XJGU8BTNDPNQJMF4XJGUUP8FC"TTFNCMZ> IUUQT TXJGUXBTNPSH