Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
TypeScriptでType Match的なことをする話 #すえなみチャンス暑気払い
Search
kyo_ago
August 04, 2019
Programming
1.4k
1
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
TypeScriptでType Match的なことをする話 #すえなみチャンス暑気払い
kyo_ago
August 04, 2019
More Decks by kyo_ago
See All by kyo_ago
フロントエンドの リソース管理の話 TechFeed Summit#1 #techfeed #techfeedsummit
kyo_ago
5
2k
WebReplayから見るWeb開発の未来 #builderscon
kyo_ago
2
1k
今日から始めるbugbounty
kyo_ago
0
320
karmaを使ったSPA向けE2Eテスト技法
kyo_ago
6
5.7k
E2Eという名称の指すもの
kyo_ago
0
2.7k
How to use Scala.js in real world?
kyo_ago
1
2.2k
Other Decks in Programming
See All in Programming
act1-costs.pdf
sumedhbala
0
210
AI がコードを書く時代における新卒エンジニアの仕事風景 (2026) / New Graduate Engineers in the Era of AI Coding (2026)
sushichan044
0
220
Vue × Nuxt × Oxc どこまで使える?実運用の現在地
andpad
0
370
Vite+ Unified Toolchain for the Web
naokihaba
0
740
関数型プログラミングのメリットって何だろう?
wanko_it
0
170
ローカルLLMでどこまでコードが書けるか -拡張版 / How much code can be written on a local LLM Extended
kishida
12
4.7k
エンジニアにデザインハーネスを 〜デザインプロセスを規定するためのハーネス〜 / Design harness from an engineer's perspective
rkaga
2
1.4k
The Bowling Game- From Imperative to Functional Programming - Part 1
philipschwarz
PRO
0
320
1年で人数1.5倍、PR数5.5倍増。 品質とアウトカムはどうなったか、 何が効いたか
ike002jp
0
120
『コードを書く以外の』エンジニアリング〜課金基盤移行プロジェクト推進のためのTips4選
yuriko1211
0
420
ローカルLLMでどこまでコードが書けるか -縮小版 / How much code can be written on a local LLM Shortened
kishida
2
190
광주소프트웨어마이스터고등학교 DevFest 특강 - 바이브 코딩 시대에서 주니어 개발자로 살아남는 방법
utilforever
1
110
Featured
See All Featured
Java REST API Framework Comparison - PWX 2021
mraible
34
9.5k
Agile Leadership in an Agile Organization
kimpetersen
PRO
0
190
Applied NLP in the Age of Generative AI
inesmontani
PRO
4
2.4k
Designing Experiences People Love
moore
143
24k
Unlocking the hidden potential of vector embeddings in international SEO
frankvandijk
0
870
Ecommerce SEO: The Keys for Success Now & Beyond - #SERPConf2024
aleyda
1
2.1k
Un-Boring Meetings
codingconduct
0
340
Crafting Experiences
bethany
1
210
SEOcharity - Dark patterns in SEO and UX: How to avoid them and build a more ethical web
sarafernandez
0
220
Skip the Path - Find Your Career Trail
mkilby
1
170
New Earth Scene 8
popppiees
3
2.4k
The MySQL Ecosystem @ GitHub 2015
samlambert
251
13k
Transcript
5ZQF4DSJQUͰ5ZQF.BUDIత ͳ͜ͱΛ͢Δ ͑͢ͳΈνϟϯεॵؾ͍ !LZP@BHP
݁
TFBMFEDMBTT PSUSBJU Λ͍·͠ΐ͏ 4DBMBͷ
ຊ
͜ΕԿʁ
5ZQF4DSJQUͷ4USJOH-JUFSBM5ZQFTΛͬͨ ঢ়ଶͷཧ$IBUXPSL$SFBUPST/PUF ͷվม൛Ͱ͢ɻ
ԿΛղܾ͍ͨ͠ͷ͔
ෳࡶͳঢ়ଶɺ݅ذΛ༰ қʹѻ͑ΔΑ͏ʹ͍ͨ͠ ʢෳࡶͳঢ়ଶɿCPPMFBOͭͱ͔Ͱཧͯ͠ΔΑ͏ͳͷʣ
۩ମతʹ
// ਏ͍ྫ class Message { constructor( private sending: boolean, private
editing: boolean, private deleted: boolean, ) {} canEdit(): boolean { if ( !this.sending && !this.deleted ) { return false; } return true; } }
͞Βʹ
// ͬͱਏ͍ྫ 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; } }
Կ͕ͳͷ͔
w ঢ়ଶͷΈ߹Θ͕ͤ݅ذΛΈͳ͍ͱஅͰ͖ͳ͍ w ݅ذͷJG͕ෳࡶ w ม͕Ճ͞Εͨ߹ͷཏੑͷอূ͕͍͠
// ਏ͍ྫ 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; } }
DMBTTNFUIPEͰ݅ذΛߦ͏ͱɺʮಛఆͷঢ় گԼͰDMBTT͕Ͳ͏͍͏ঢ়ଶʹͳΔ͔ʁʯͷஅ͕͘͠ ͳΔɻ ݅ذͦͷঢ়ଶʹ໊લΛ͚ͭͳ͍ͨΊɺʮ݅ذ ʹҰக͢ΔͷͲ͏͍͏ঢ়ଶ͔ʁʯʹର͢Δղऍ͕ᐆດ ʹͳΔɻ ʢ͜ͷ߹ɺDBO&EJUʮฤूՄೳͳঢ়ଶʯͰҰக͢Δ ͱࢥ͏͚Ͳʣ
// ֎෦͔Βݟͯಉ͡ঢ়ଶ͔ʁ const message = new Message(/.../); const getEditableMessage =
() => { return message.canEdit() ? "मਖ਼Ͱ͖ΔΑʂ" : "मਖ਼ Ͱ͖ͳ͍Αʂ"; } const showEditableIcon = () => { return message.canEdit(); }
ྫ͑ɺ৽͘͠ʮJT4FDSFUʯͱ͍͏มΛՃ͠ ͨ߹ɺDBO&EJUʹӨڹ͢ΔͩΖ͏͔ʁ ·ͨɺӨڹ͢Δͱͯ͠ɺDBO&EJUΛݺͼग़͍ͯ͠ Δଆಉ͡ఆͩΖ͏͔ʁ
Ͳ͏ղܾ͢Δͷ͔
ঢ়ଶΛDMBTTԽ͢Δ
// ָͳྫ type MessageStateLiterals = "sending" | "loading" | "editing"
| "deleted"; class MessageState { private state: MessageStateLiterals; constructor( sending: boolean, loading: boolean, editing: boolean, deleted: boolean, ) { // ݅ʹԠͯ͡this.stateMessageStateLiteralsͷ͍ͣΕ ͔Λೖ } match<R>(matcher: { [key in MessageStateLiterals]: () => R }): R { return matcher[this.status](); } }
͍ํ
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, }); } }
ར
ར w ঢ়ଶͷஅʹؔΘΔϩδοΫ͕ू͞ΕΔ w ঢ়ଶͷՃ࣌ʹطଘͷ࣮ʹରͯ͠ཏੑΛڧ੍Ͱ͖Δ w ঢ়ଶʹର໊ͯ͠લ͕ͭ͘
ϩδοΫ͕ू͞ΕΔ // ਏ͍ྫ 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() { // ঢ়ଶͷมߋ͕෦ʹӅṭ͞Ε͓ͯΓɺςετ͕͍͠ } }
ϩδοΫ͕ू͞ΕΔ // ָͳྫ type MessageStateLiterals = "sending" | "loading" /…/;
class MessageState { private state: MessageStateLiterals; constructor( sending: boolean, loading: boolean, editing: boolean, deleted: boolean, ) { // ݅ʹԠͯ͡this.stateMessageStateLiteralsͷ͍ͣΕ͔Λೖ } match<R>(matcher: { [key in MessageStateLiterals]: () => R }): R { return matcher[this.status](); } }
ϩδοΫ͕ू͞ΕΔ // ָͳྫʢςετʣ 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); }); }); });
ঢ়ଶͷՃ࣌ʹطଘͷ࣮ʹରͯ͠ ཏੑΛڧ੍Ͱ͖Δ // matcherʹશͯͷύλʔϯͷkeyΛίϯύΠϥϨϕϧͰڧ੍ match<R>(matcher: { [key in TypeLiterals]: ()
=> R }): R { return matcher[this.status](); }
ঢ়ଶͷՃ࣌ʹطଘͷ࣮ʹରͯ͠ ཏੑΛڧ੍Ͱ͖Δ // 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! });
ঢ়ଶʹର໊ͯ͠લ͕ͭ͘ // ਏ͍ྫ class Message { constructor( private sending: boolean,
private editing: boolean, private deleted: boolean, ) {} canEdit(): boolean { // canEditʮฤू͕Մೳʯͱ͍͏݁ՌΛฦ͍ͯ͠Δ͚ͩͰɺ // ͦͷ࣌ͷMessageͷঢ়ଶද͍ͯ͠ͳ͍ɻ } }
DBO&EJU͓ͦΒ͘ʮฤूՄೳͳঢ়ଶʯͱݴ͍͍ͬͯ ͕ɺࢀর͢Δม͕૿͍͑ͯ͘ͱʮಛఆͷঢ়ଶͱͲ͏ ͍͏มͷΈ߹Θͤͳͷ͔ʁʯͷѲ͕͘͠ͳͬͯ ͘Δɻ ʮಛఆͷ݅ʹର͢ΔมͷΈ߹Θͤʯ͕ෆ໌ͳঢ়ଶ ͕ଓ͘ͱɺͦͷ͏ͪʮݱঢ়ͷมͷΈ߹ΘͤΛಛఆͷ ݅ͱ͢ΔʯΑ͏ʹͳΓɺ࣮͕༷Խ͢Δɻ ʢ͔͜͠ΕϦϑΝΫλϦϯάʹϦεΫΛ͍ɺ*%& ͷαϙʔτ͕͋ͬͯਖ਼֬ͳमਖ਼͕ࠔʣ ࣄલʹঢ়ଶΛΓग़͠ɺ໌֬ͳ໊લΛ͚ͭΔ͜ͱͰ࣮
ͷ༷ԽΛࢭ͠ɺमਖ਼ͷ༨Λ͢͜ͱ͕Ͱ͖Δɻ
Ͳ͏͍ͬͨ߹ʹ͏ ͷ͔
Ͳ͏͍ͬͨ߹ʹ͏ͷ͔ w 7BMVF0CKFDUQBUUFSOͷҰ෦ͱͯ͠ w ͦΕͧΕͷ݅ʹ໊ؔͯ͠લ͕ͭ͘ ʢϢϏΩλεݴޠʣ
ࠜຊతʹղܾ͍ͨ͠ Կ͔ʁ
ղܾ͍ͨ͠ w ਖ਼͘͠ྨ͢Δ w ਖ਼໋໊͘͢͠Δ
ʮ݅ذͷ݁Ռͱͯ͠ͷྨʯͰͳ͘ɺ༗ݶݸͷ ྨͷҰͭͱͯ͠ॴଐΛׂΓͯΔɻ ʮૹ৴தϑϥάͱฤूࡁΈϑϥά͕Φϯͷϝοηʔδʯ Ͱͳ͘ɺʮ্ॻ͖ૹ৴தϝοηʔδʯͱͯ͠ڍಈΛׂ ΓͯΔɻ ʮ݅ذͷ݁ՌͷҰ෦ʯͰͳ͘ɺذͷ݁Ռʹର ໊ͯ͠લΛ͚ͭΔɻ ʮૹ৴ऀ͕ۭͰຊจ͕͋Δ߹݅ذ͢ΔʯͷͰͳ ͘ɺʮૹ৴ऀ͕ۭͰຊจ͕͋Δ߹γεςϜϝοηʔδ ͱ໋໊ͯ͢͠Δʯ͜ͱͰɺҎޙʮγεςϜϝοηʔ
δʯʹରͯ͠ॲཧΛߦ͏
࣮ͷ؆ུԽ
ҎԼͷΑ͏ͳ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()](); } }
ҎԼͷΑ͏ͳDMBTTΛఆٛ͢Δ͜ͱ Ͱ࣮Λ؆ུԽͰ͖Δ class MessageState extends BaseType<"sending" | /.../ > {
constructor( sending: boolean, loading: boolean, /.../ ) { if (sending) { return super("sending"); } // ඞཁͳذΛՃ } }
2"
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
͓ΘΓ