TypeScriptによるプロダクト開発の知見
Product development with TypeScript
View Slide
Name!CSO 5BLFUPTIJ"POP੨݈རOccupation'SPOUFOE%FWFMPQFS1SPEVDU0XOFSCompany$ZCFSBHFOU"EUFDI4UVEJP"*.FTTFOHFSOSS$POUSJCVUPSPG7AboutIUUQJOGPCODI
1SPEVDUEFWFMPQFEFGGFDUJWFMZ5ZQF4DSJQUಋೖͯ͠ϓϩμΫτ։ൃͷੜ࢈ੑ্͕Γ·͔ͨ͠ʁಋೖ͚ͨͩ͠Ͱͳ͔ͳ͔͍͔͠͠Ε·ͤΜɻࠓͷτʔΫ͕ੜ࢈ੑΛͰ͖Δ্͚ͩ͛ΔͨΊͷΨΠυͱͳΔ͜ͱΛئ͍·͢ɻ
*NQPSUBOUUIJOHTBCPVU5ZQF4DSJQU5ZQF4DSJQUͷॏཁͳͰ͢KBWBTDSJQUͰͳ͍͕KBWBTDSJQUͰ͋Δܕ࣮ߦ࣌ʹফ͑Δ
ػೳ
#FGPSFBMMલఏͱͯ͠UTDPOpHKTPOͰҎԼͷػೳඞͣ0/ʹ͠·͠ΐ͏OP*NQMJDJU"OZTUSJDU
*NQPSUBOUUIJOHTBCPVU5ZQF4DSJQUKBWBTDSJQUͰͳ͍͕KBWBTDSJQUͰ͋Δ5ZQF4DSJQUKBWBTDSJQUͷ4VQFS4FUΛᨳ͍ͬͯ·ͦ͢ͷͨΊ༷తʹ+BWBTDSJQUΛ౿ऻ͍ͯ͠·͕͋͘͢·Ͱผͷݴޠͱଊ͑ͨ΄͏͕ྑ͍Ͱͦ͢ͷ্Ͱ͍͔ͭ͘ॏཁͳʹ͍͓ͭͯ͠·͢
FOVNFOVN5ZQF4DSJQUͷಠࣗػೳͰ͢ KBWBTDSJQUʹແ͍ͱ͍͏ҙຯࠓޙಉ͡ҙຯͰ͍·͢
enum Card {SPADE,HEART,DIAMOND,CLUB}// ࿈൪assert.equal(Card.SPADE, 0);assert.equal(Card.DIAMOND, 2);// ٯҾ͖assert.equal(Card[Card.DIAMOND], 'DIAMOND');
enum Event {CLICK = 'CLICK',MOUSEOVER = 'MOUSEOVER';}
FOVNFOVN͜ͷΑ͏ʹΛ·ͱΊΔͷʹར༻͠·͕͢FOVN͕ࣗܕΛߏ͠ɺܕνΣοΫʹར༻Ͱ͖·ͨͩ͢͠ҙ͕͋Γ·͢
enum Card {SPADE,HEART,DIAMOND,CLUB}const fn = (card: Card) => { return card; }fn(Card.SPADE); // OKfn(0); // OKfn(10); // OK
FOVNFOVNΛͦͷ··ར༻͢Δͱɺ୯७ͳOVNCFSܕͱͳΓͷൣғΛνΣοΫͯ͘͠Ε·ͤΜͦͷͨΊෆਖ਼ͳΛ͢͜ͱՄೳʹͳΓ·͢
enum EventType {CLICK = 'CLICK',MOUSEOVER = 'MOUSEOVER';}const fn2 = (event: EventType) => {return event;}fn2(EventType.CLICK);fn2(EventType.MOUSEOVER);fn2("MOUSEDOWN"); // NG
FOVNจࣈྻΛͬͨ߹ʹਖ਼͘͠FOVNʹఆٛ͞Ε͍ͯΔͷνΣοΫΛߦ͍·͢·ͨΛܕͱͯ͠͏͜ͱͰ5BHHFE6OJPOͱͯ͠͏͜ͱ͕Ͱ͖·͢
enum EventType {CLICK = 'CLICK',MOUSEOVER = 'MOUSEOVER';}interface ClickEvent {type: EventType.CLICK;}interface MouseDownEvent {type: EventType.MOUSEOVER;}declare const clickEvent: ClickEvent;declare const mouseDownEvent: MouseDownEvent;const fn3 = (event: ClickEvent) => { return event; }fn3(clickEvent); // OKfn3(mouseDownEvent); // NG
FOVNFOVNศརͰ͕͢ɺͷ߹ܕ͚ͩͰ੍ޚͰ͖ͳ͍ͷͰɺͱͯ͠ར༻͢Δ߹ݺͼग़͞ΕΔଆͰൣғͷνΣοΫΛ͠·͠ΐ͏ɻ
/BNFTQBDFOBNFTQBDF5ZQF4DSJQUಠࣗػೳͰ͕͢&4.PEVMFTͷ͓͔͛Ͱ༨Γ͏ҙຯͳ͘ͳΓ·ͨ͜͠ͷػೳએݴϑΝΠϧҎ֎Ͱ͏ͷࠞཚͷݩͳͷͰ͏ඞཁͳ͍Ͱ͠ΐ͏
/VMMPSVOEFpOFEKBWBTDSJQUʹͭͷແޮͳ͕͋Γ·͢OVMMͱVOEFpOFEͰ͢VOEFpOFE͕ະఆٛͷ߹ʹར༻͞ΕɺOVMM݁Ռ͕ଘࡏ͠ͳ͍࣌ʹΘΕ·͕ͦ͢ͷ͍͚ʹରͨ͠ҙຯ͋Γ·ͤΜࠞཚ͢Δ͚ͩͰ͢
*O5ZQF4DSJQUOVMMVOEFpOFE5ZQF4DSJQU্Ͱ໘Ͱ͢TUJSDU/VMM$IFDLT0/ʹ͖͢Ͱ͕͢ɺVOEFpOFEͷνΣοΫ͞Ε·ͤΜ
.BZCFPS0QUJPOBMOVMMɺVOEFpOFEXSBQͯ͠͠·͍·͠ΐ͏ɹ
type Optional = T | undefined | null;
0QUJPOBMຊདྷ0QUJPOBMΫϥεʹͯ͠͠·͍͍ͨͱ͜ΖͰ͕͢5ZQF4DSJQUͰ0QUJPOBM$IBJOJOH͕ೖΔͷͰΑΓ5ZQF4DSJQUωΠςΟϒͰऔΓѻ͑Δϑϥοτͳܕͷ΄͏͕ྑ͍Ͱ͠ΐ͏
type Optional = T | undefined | null;declare const obj: Optional<{value?: string}>;// ·ͩಈ͖·ͤΜ// orobj?.value || ‘’// forceobj!.value!
࣮
%FWFMPQXJUI5ZQF4DSJQU5ZQF4DSJQUΛͬͯ։ൃ͢Δ߹ͷ࣮खॱKBWBTDSJQUͱେ͖͘ม͑·͠ΐ͏
*OUFSGBDF'JSTU࠷େࣄͳ͜ͱநతͳ࣮Λ࠷ॳʹߦ͏͜ͱͰͦ͢Εͭ·Γ۩ମతͳϩδοΫ͔ΒΕΔ͜ͱͰ͢5ZQF4DSJQUJOUFSGBDFͱUZQFʹΑͬͯͦΕΛՄೳʹ͍ͯ͠·ِ͢3FEVYΈ͍ͨͳྫΛհ͠·͢
interface Todo {id: string;date: Date;title: string;done: boolean;}type Todos = Todo[];interface State {todos: Todos;}type Payload = {type: "ADD",} | {type: "DELETE"payload: {id: string};}const reducer = (payload: Payload, state: State): State => {return state;}
-PHJDJTMBUFSSFEVDFSͷ࣮Ҏ֎શͯܕͷΈͰ࣮͞Ε͍ͯ·͢SFEVDFSதۭͰΛฦ͚ͩ͢Ͱ͔͢͜͠͠ΕͰϓϩάϥϜͷࠎͰ͖͍ͯ·͋͢ͱSFEVDFSͷ࣮ʹूத͢Δ͚ͩͰ͢
-PHJDJTMBUFSۃͳ۩ମతͳΞϧΰϦζϜ͋·ΓॏཁͰ͋Γ·ͤΜσʔλߏɾܕ͕ͬͱॏཁͰ͕ܾ͢͜͜·Εେ֓ΞϧΰϦζϜࣗવͱಋ͔Ε·͢
ڥք
7JSUVBMBOE3FBM࠷ॳʹड़ͨΑ͏ʹ5ZQF4DSJQU͋͘·ͰԾͷܕΛ࣋ͭݴޠͰͦ͢ͷͨΊɺ࣮ࡍʹ࣮ߦ͞ΕΔ߹ʹͱܕͷဃ͕ى͖ΔՄೳੑ͕͋Γ·͢
4FQBSBUFE8PSMEಛʹဃ͕ى͖͍͢ͷ͕ɺ֎෦ͱͷڥքͱͳΔ෦Ͱ͢"1*αʔόͱͷڥքϒϥβετϨʔδͱͷڥք
"EBQUFSͨͩ͠ɺٯʹݴ͑͜ΕΒͷڥք͑͑ͯ͞ɺઌఔͷJOUFSGBDFઌߦͷ࣮Λߦ͑ဃΛΒ͢͜ͱ͕Ͱ͖ΔͷͰͳ͍Ͱ͠ΐ͏͔ʁ·ͨ1SPUP#VGͷΑ͏ͳ54ࣗಈੜܥΛ͏ͷखͰ͢
1SPHSBN1SPHSBN1SPHSBN"EBQUFS&YUFSOBM*OUFSGBDF
$MFBO"SDIJUFDUVSF·ΔͰ$MFBO"SDIJUFDUVSFͷϨΠϠʔਤͷΑ͏ʹͳΓ·͔ͨ͠͠͠ɺผʹ$MFBO"SDIJUFDUVSFΛར༻͠Ζͱݴ͍ͬͯΔΘ͚Ͱͳ͘5ZQF4DSJQUͷԾܕͱ͍͏Έʹͱͬͯ"EBQUFSඇৗʹॏཁͳҙຯΛ࣋ͪ·͢ͲΜͳΞʔΩςΫνϟΛ࠾༻͍ͯͯ͠֎෦γεςϜڥքΛਖ਼͑͘͠Δ͜ͱͰɺ5ZQF4DSJQUͷܕΑΓҰཱͭͰ͠ΐ͏
5P5ZQFEࠓ·Ͱͷ࠷ॳ͔Β5ZQF4DSJQUͰ։ൃ͍ͯ͠ΔέʔεΛલఏͱ͍ͯ͠·͕͢͠طଘͷKBWBTDSJQU͔ΒҠߦ͢Δ߹֎෦ڥք͔ΒܕΛ͚͍ͯ͘ͱΑ͍Ͱ͠ΐ͏·ͨɺਖ਼͍͔ͨ͠ͱਖ਼͘͠ͳ͍ܕΛڥքΛه͓ͯ͘͜͠ͱ͕େࣄͰ͢
"OZBEBQUFSܕ͚͞Ε͍ͯͳ͍ةݥͳྖҬશͯɺਖ਼͍͠ܕ͔Βมͨ͠ͱ͍͏ҹΛ͚͓ͭͯ͘ͱϦϑΝΫλϦϯά͕͍͢͠Ͱ͢
type UncheckedFixme = any;interface Model {}const unTypedFn = (a: UncheckedFixme) {}
"OZBEBQUFS6ODIFDLFE'JYNF5͕ͲΜͲΜίʔυͷίΞྖҬʹԡ͠ࠐ·ΕΔ·ͰϦϑΝΫλϦϯά͚ͭͮ͠·͠ΐ͏ͦͯ͠࠷ޙʹίʔυத͔Βফͯ͠͠·͍·͠ΐ͏͜ͷΑ͏ͳ#PUUPN6Qతͳܕͷ͚ํΛ͢Δͱɺ֎෦ͷةݥͳྖҬ͔Β҆શͳ෦ഭ͍ͬͯ͘ͷͰܕͷϛε͕ى͖ͮΒ͍Ͱͪ͢ͳΈʹ6ODIFDLFE'JYNF5-JOUΤϥʔग़ΔՄೳੑ͕͋ΔͷͰ௵͓͍͍ͯͯͩ͘͠͞
ίʔσΟϯά
5ZQFJTCPUIFSܕ͚݁ߏ໘Ͱ͢࠷ॳʹJOUFSGBDFͰܕఆٛΛͪΌΜͱ͓ͯ͘͠ͱزจ͔ղফ͞Ε·͕͢ຊ࣭తʹՃతͳ࡞ۀͳͷͰαϘΓ͕ͪͰ͢
.BZCFXSPOHͨͩɺܕ͚ͷ໘͘͞͞ίʔυͷ·͔ͣ͞͠Ε·ͤΜ
class Test {private value: {[key: string]: string} | null;constructor() {}public setValue(value: {[key: string]: string}) {this.value = value;}public doSomething() {return this.value!['key'] // you need to through type check.}}
*OJUJBMJ[FMBUFS͜ͷΑ͏ͳޙʹॳظԽ͞ΕΔϝϯόมܕͰදݱ͢Δͷ͕ࠔͰ͢͞Βʹ/VMMBCMF͔VOEFpOFEΛڐ༰͢Δܕʹͨ͠߹ʹ͍͍ͪͪΛॻ͘ͷةݥ͔ͭ໘Ͱ͢͜ͷΑ͏ͳɺͦͦܕͰදݱ͢Δͷ͕͍͠ίʔυΛආ͚ΔΑ͏ʹ͢Δͷ͕ྑ͍Ͱ͢ࠓճͷέʔεͰݴ͑શͯDPOTUSVDUPSͰॳظԽ͖͢Ͱ͢
function getValue() {let value: {[key: string]: number} | null = null;if (x) {value = {key: 1};} else {value = {value: 1};}return value;}
*OJUJBMJ[FMBUFSJGจͷதͰมΛॳظԽ͢ΔͷΛ͚ͯ͞ɺJGจΛؔԽͯ͠͠·͍·͠ΐ͏
.PSFUZQBCMFDPEFΑΓγϯϓϧʹܕ͚Ͱ͖ΔίʔυਓؒʹಡΈ͘͢ཧղ͍͢͠ίʔυͱͳΓ·ͪ͢ΖΜ͖Ε͍ʹॻ͍ͯෳࡶͳܕΛ࣋ͭέʔε͋Γ·͢͠ɺશ͕ͯͦͷͱ͓ΓͰ͋Γ·ͤΜ͕ෳࡶͳܕػೳΛ͏લʹίʔυͷߏ͕ਖ਼͍͔֬͠ೝͯ͠Έ·͠ΐ͏
·ͱΊ5ZQF4DSJQUͰͷ։ൃʹ͍ͭͯͷτʔΫͰͨ͜͠ΕΒͷ5JQT͕͋ͳͨͷ։ൃͷखॿ͚ʹͳΔͱ͍Ͱ͢͝ਗ਼ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠