安全性の極北から見るTypeScript

993ef638264def4334358f986fb4a91d?s=47 uhyo
October 02, 2019

 安全性の極北から見るTypeScript

TypeScript meetup #3の発表資料です。

993ef638264def4334358f986fb4a91d?s=128

uhyo

October 02, 2019
Tweet

Transcript

  1. 㸜Ⰻ䚍ך噰⻌ַ׵鋅׷ 5ZQF4DSJQU 5ZQF4DSJQU.FFUVQ

  2. 荈䊹稱➜ –ؿٗٝزؒٝسؒٝآص،倜⼼䎃湡!-*/&吳䒭⠓爡 – 5ZQF4DSJQU娖䎃⼱ֻ׵ְ –2JJUB蔓➂5ZQF4DSJQUةؚDPOUSJCVUJPO侧⡘ – ! չ5ZQF4DSJQUך㘗ⰅꟌպ – "չ侁⻌罏ך5ZQF4DSJQUպ

    – # չ5ZQF4DSJQUך㘗䱿锷鑫铡պ זו !VIZP@
  3. !ⴱ׭גך涫㠡"

  4. 5ZQF4DSJQU׾⢪ֲאך椚歋 –㸜Ⰻ䚍 ˘ 㘗ؒٓ٦׾嗚⳿׃גغؚ׾劢搫ח꣇־׷ –Ꟛ涪⸬桦˘ 㘗䞔㜠׾欽ְ׋酡㸣ד⸬桦״ֻꟚ涪דֹ׷ 㘗ָسًُؗٝزחז׷ ˟5ZQF4DSJQUדכ؝٦س欰䧭ח㘗䞔㜠כ⢪׻׸זְ

  5. 5ZQF4DSJQU׾⢪ֲאך椚歋 –㸜Ⰻ䚍 ˘ 㘗ؒٓ٦׾嗚⳿׃גغؚ׾劢搫ח꣇־׷ –Ꟛ涪⸬桦˘ 㘗䞔㜠׾欽ְ׋酡㸣ד⸬桦״ֻꟚ涪דֹ׷ 㘗ָسًُؗٝزחז׷ ˟5ZQF4DSJQUדכ؝٦س欰䧭ח㘗䞔㜠כ⢪׻׸זְ

  6. 5ZQF4DSJQUך㸜Ⰻ䚍ה؝أز 㸜Ⰻ䚍 㘗ך׋׭ך 㷕统؝أز Ꟛ涪؝أز 넝 搀 • 5ZQF4DSJQUכ㸜Ⰻ䚍׾锃眍〳腉 •

    㸜Ⰻ䚍׾♴־׷ --noImplicitAnyך搀⸬⻉ anyas@ts-ignoreזו • 㸜Ⰻ䚍׾♳־׷ VOJPO㘗װ勴⟝㘗זוך崞欽 • 㸜Ⰻ䚍׾넝׭׷קו؝أز׮♳傻 㘗ػؤٕ׾װ׶ֺׅ׷ה؝أػָ䝤⻉
  7. 㘗ך؝أزػؿؓ٦وٝأ 㸜Ⰻ䚍 㘗ך׋׭ך 㷕统؝أز Ꟛ涪؝أز 넝 搀 const items =

    { apple: { price: 128, }, orange: { price: 200, taxRate: 0.1 } } const getPriceIncludingTax = (name, taxRate) => { const item = items[name] taxRate = taxRate || item.taxRate if (taxRate == null) throw new Error("OMG") return item.price * taxRate } 㸜Ⰻ䚍搀 !
  8. 㘗ך؝أزػؿؓ٦وٝأ 㸜Ⰻ䚍 㘗ך׋׭ך 㷕统؝أز Ꟛ涪؝أز 넝 搀 const items: Record<string,

    { price: number; taxRate?: number }> = { apple: { price: 128 }, orange: { price: 200, taxRate: 1.1 } } const getPriceIncludingTax = (name: string, taxRate?: number) => { const item = items[name] taxRate = taxRate || item.taxRate if (taxRate == null) throw new Error("OMG") return item.price * taxRate } 㸜Ⰻ䚍⚥ ! !
  9. 㘗ך؝أزػؿؓ٦وٝأ 㸜Ⰻ䚍 㘗ך׋׭ך 㷕统؝أز Ꟛ涪؝أز 넝 搀 type ItemName =

    'apple' | 'orange' const assertIsItemPriceData = <D extends Record<ItemName, { price: number; taxRate?: number }>>(data: D) => data const items = assertIsItemPriceData({ apple: { price: 128 }, orange: { price: 200, taxRate: 1.1 } }) type Distribute<N extends ItemName> = N extends ItemName ? ((typeof items)[N] extends { taxRate: number } ? (name: N) => number : (name: N, taxRate: number) => number) : never type UtoI<U> = (U extends any ? (arg: U) => void : never) extends (arg: infer I) => void ? I : never type getPriceIncludingTaxFunc = UtoI<Distribute<ItemName>> const getPriceIncludingTax: getPriceIncludingTaxFunc = ( name: ItemName, taxRate?: number ) => { const item = items[name] as any taxRate = (taxRate || item.taxRate) as number return item.price * taxRate } 㸜Ⰻ䚍넝 ! !
  10. 5ZQF4DSJQUך㸜Ⰻ䚍ה؝أز 㸜Ⰻ䚍 㘗ך׋׭ך 㷕统؝أز Ꟛ涪؝أز 넝 搀 غٓٝأ׾罋ִ״ֲ • 㸜Ⰻ䚍ָו׸ֻ׵ְ妜׃ְךַ

    וך玎䏝ך⽬ꤹ䚍׾鏩㺁ׅ׷ךַ • ؝أز׾ו׸ֻ׵ְ䩪ִ׷ךַ • 剑穄湡垥כוֿזךַ 䖓ַ׵54׾㼪Ⰵׅ׷㜥さ
  11. 5ZQF4DSJQUך㸜Ⰻ䚍ה؝أز 㸜Ⰻ䚍 㘗ך׋׭ך 㷕统؝أز Ꟛ涪؝أز 넝 搀 !ֿךز٦ؙכֿֿדׅ

  12. 5IJT5BML 㸜Ⰻ䚍ח噰䮶׶׃׋5ZQF4DSJQU錁׾䋒侄׃תׅ! –㸜Ⰻ䚍הךぢֹさְ倯 –⡦ָ㸜Ⰻ䚍׾腘ַׅךַ –וך״ֲח㸜Ⰻ䚍׾㸚׷ךַ

  13. 㸜Ⰻ䚍ַ׵鋅׷5ZQF4DSJQU 5ZQF4DSJQUכ⨳Ⰻ䚍ך瑎ָ㢳ְ –孡鯪ח㎳׾אֻֿהָדֹ׷anyװasזו –׉׮׉׮㘗ءأذي荈⡤ח׮瑎ָ֮׷ ⵃ⤑䚍娖〷涸穗箮㹋鄲♳ך㉏겗ך׋׭ DG5ZQF4DSJQUךVOTBGFז乼⡲תה׭ ˊ 2JJUB CZ!LHULS ˟⨳Ⰻ䚍

    TPVOEOFTT 㘗ثؑحؙ׾鸐׸ל㹋遤儗ח㘗ؒٓ٦ָ饯ֹזְ䚍颵
  14. 㸜Ⰻ䚍ַ׵鋅׷5ZQF4DSJQU 5ZQF4DSJQUכ⨳Ⰻ䚍ך瑎ָ㢳ְ –孡鯪ח㎳׾אֻֿהָדֹ׷anyװasזו –׉׮׉׮㘗ءأذي荈⡤ח׮瑎ָ֮׷ ⵃ⤑䚍娖〷涸穗箮㹋鄲♳ך㉏겗ך׋׭ ⨳Ⰻ䚍ך瑎׾㝰ֺ㸜Ⰻ䚍׾㸚׷ךכ䧮ղך䕵湡!

  15. ⡭锑ז׈㸜Ⰻ䚍חֿ׌׻׷ךַ  㘗ءأذيך䠐㄂הכ⨳Ⰻ䚍ך椚锷涸⥂鏾זךד  㸜Ⰻ䚍׾灶㠨ׅ׷ה  㘗ءأذيך䠐㄂ָ搀חז׷ַ׵ ˟植㹋ך鎉铂׾⵸חׅ׷ה椚锷涸⥂鏾הְֲַךכ㣄暟铂זךדָׅ׉ֿכ噰䮶׶ח⯜ׄג⹞䒔׃גֻ׌ְׁ

  16. 㸜Ⰻ䚍׾㸚׷חכ 㣐⾱⵱ ㎳׾אַזְ㣐⾱⵱ 㘗ءأذيך瑎׾瑱ְ׋㜥さכⴽזךד׉ֿכⴽ鷿孡׾אֽת׃׳ֲ

  17. ⡦ָ㸜Ⰻ䚍׾腘ַׅךַ ֮׷ְכ䧮ղכוך״ֲח㎳׾אֻךַ

  18. BOZ㘗 ! BOZ㘗ך暴䗙 –BOZ㘗ך⦼כ⟣䠐ךⵃ欽ָ〳腉 ⡦׾׃ג׮㘗ؒٓ٦ָ饯ֹזְ –BOZ㘗ך⦼ַ׵䖤׋ⴽך⦼׮BOZ㘗׾䭯א فٗػذ؍،ؙإأ٥ꟼ侧ㄎן⳿׃ך鵤׶⦼זו ⚠ BOZ㘗ך⽬ꤹ䚍 –׮כװ㶷㖈荈⡤ָ㎳

    –㎳ָ⠗厩ׅ׷ BOZ寅厩 ˟֮ה㘗䞔㜠ָ#חז׷ךד酡㸣׮⸬ֹתׇ׿
  19. BOZ寅厩☠ const obj: any = doSomeMagic(); const res = obj.hello.world();

    const num: number = obj; const str: string = obj; function doSomeMagic(){ /* empty */ } BOZ㘗ך㢌侧PCK׾⡲䧭 BOZ㘗ך⦼כוֲ⢪׏ג׮ ؒٓ٦חז׵זְ BOZ㘗׾侧⦼װ俑㶵⴨ה׃ג 䪔׏ג׮ؒٓ٦חז׵זְ
  20. BOZ寅厩☠ const obj: any = doSomeMagic(); const res = obj.hello.world();

    const num: number = obj; const str: string = obj; function doSomeMagic(){ /* empty */ } ֿךPCK׏ג㢌侧כו׿ז괏ח ⢪׏ג׮ؒٓ٦חז׵זְ׃ 侧⦼ד׮֮׷׃俑㶵⴨ד׮֮ ׷׿أ״XXXXXXXXXXXX 㣐㎳ 寅厩ׁ׸׋㢌侧BOZ寅厩彁 鵤׶⦼כBOZ㘗✳如寅厩 BOZך鄃㹱 儗꟦䊴余䷼
  21. BOZ寅厩☠ const obj: any = doSomeMagic(); const res = obj.hello.world();

    const num: number = obj; const str: string = obj; function doSomeMagic(){ /* empty */ } 寅厩眔㔲 BOZ㘗ך㢌侧ָ֮׷ أ؝٦فⰋג BOZ׾ꅿ佝׃חׅ׷ה ꬊ䌢ח⽬ꤹ 寅厩ׁ׸׋㢌侧BOZ寅厩彁 ˟BOZכ鸬ꓲ涸חOP*NQMJDJU"OZؒٓ٦׾涪欰ׇׁ׷挿׮⳷䝤
  22. BTח״׷㘗ٍؗأز ! BTח״׷㘗ٍؗأزך堣腉 –䒭ך㘗׾ⴽך㘗חتٍؐٝؗأزדֹ׷ ˟㘗䞔㜠ָ㢌׻׷׌ֽկ㹋遤儗כ⡦׮饯ֹזְ ⚠ BTח״׷㘗ٍؗأزך⽬ꤹ䚍 –㎳׾אְג׮؝ٝػ؎ٓח䙫׵׸זְ ˟as constכasؗ٦ٙ٦س׾⢪׏גְ׷ֽוⴽ暟ד㸜Ⰻזךדו׿ו׿⢪ְת׃׳ֲկ

  23. BTח״׷㘗ٍؗأزך⢽  const v = getNumberOrString() as string; console.log(v.slice(0, 10));

    function getNumberOrString(): number | string { return Math.random() ? "foo" : 123; } number | string㘗ך䒭׾ string㘗חتٍؐٝؗأز
  24. BTח״׷㘗ٍؗأزך⢽  const v = getNumberOrString() as string; console.log(v.slice(0, 10));

    function getNumberOrString(): number | string { return Math.random() ? "foo" : 123; } 鵤׶⦼OVNCFS]TUSJOHהַ 鎉׏ג׷ֽווְֲׇא׮ TUSJOHד׃׳XXXXXXXX 㣐㎳ BTך鄃㹱㹋遤儗ؒٓ٦
  25. BTך䕦갟眔㔲 const v = getNumberOrString() as string; console.log(v.slice(0, 10)); function

    getNumberOrString(): number | string { return Math.random() ? "foo" : 123; } ㎳ָ㢌侧ח⥂㶷ׁ׸׋ ךד׉׸⟃꣬Ⰻגָ ㎳ך䕦갟眔㔲ח
  26. BTח״׷㘗ٍؗأزך⢽  function getAnchorOfNode(node: HTMLElement): HTMLAnchorElement | null { let

    current: HTMLElement | null = node; while (current) { if (current.tagName === "A") { return current as HTMLAnchorElement; } current = current.parentNode as HTMLElement | null; } return null; }
  27. BTח״׷㘗ٍؗأزך⢽  function getAnchorOfNode(node: HTMLElement): HTMLAnchorElement | null { let

    current: HTMLElement | null = node; while (current) { if (current.tagName === "A") { return current as HTMLAnchorElement; } current = current.parentNode as HTMLElement | null; } return null; } current.parentNodeך㘗׾ Node & ParentNode | nullַ׵ HTMLElement | nullח⥜姻 ؒحآ؛٦أ׾ꤐֽל姻׃ְ
  28. BOZהBTךתה׭ –姻׃ְ倯ぢח㘗׾⥜姻ׅ׷׋׭ח⢪ֲֶ –㎳׾㢌侧ח⥂㶷׃׋儗挿ד،ؐز –BOZכװלְ㎳ד׃ַ׮⠗厩ׅ׷

  29. וך״ֲח㸜Ⰻ䚍׾㸚׷ךַ

  30. 㸜Ⰻ䚍׾㸚׷倯岀 㣐⾱⵱ ㎳ח״׷寅厩眔㔲׾杞ֻׅ׷

  31. 㸜Ⰻ䚍׾㸚׷倯岀 㣐⾱⵱ ㎳ח״׷寅厩眔㔲׾杞ֻׅ׷ ➂꟦ָ顑⟣׾䭯א眔㔲׾杞ֻׅ׷  㢌侧ח㎳׾⥂㶷׃זְ ⽬ꤹ䚍׾䒭חꟗׄ鴥׭׷㢌侧ٖكٕך姻׃ׁ׾➂ָ䬐⥂  ꟼ侧ך؎ٝة٦ؿؑ٦أד㎳׾אַזְ ⽬ꤹ䚍׾ꟼ侧ⰻחꟗׄ鴥׭׷ꟼ侧ٖكٕך姻׃ׁ׾➂ָ䬐⥂

    ׉׸ָ搀椚ז׵
  32. 㢌侧ח㎳׾⥂㶷׃זְ function getAnchorOfNode(node: HTMLElement): HTMLAnchorElement | null { let current:

    HTMLElement | null = node; while (current) { if (current.tagName === "A") { return current as HTMLAnchorElement; } current = current.parentNode as HTMLElement | null; } return null; }
  33. 㢌侧ח㎳׾⥂㶷׃זְ function getAnchorOfNode(node: HTMLElement): HTMLAnchorElement | null { let current:

    HTMLElement | null = node; while (current) { if (current.tagName === "A") { return current as HTMLAnchorElement; } current = current.parentNode as HTMLElement | null; } return null; } BTך⽬ꤹ䚍ך䕦갟眔㔲 ֿך㘗䓼ⵖָ姻׃ְֿה׾➂꟦ָ⥂鏾ׅ׸ל0, ֿֿכ姻׃ְ
  34. ꟼ侧ך؎ٝة٦ؿؑ٦أד㎳׾אַזְ interface Foo { type: 'foo' } function isFoo(obj: any):

    obj is Foo { return obj != null && obj.type === 'foo' } ♷ִ׵׸׋⡦׵ַך⦼objָ Foo㘗ַוֲַⴻ㹀ׅ׷ꟼ侧
  35. ꟼ侧ך؎ٝة٦ؿؑ٦أד㎳׾אַזְ interface Foo { type: 'foo' } function isFoo(obj: any):

    obj is Foo { return obj != null && obj.type === 'foo' } BOZך䕦갟眔㔲כꟼ侧ⰻⰋג ꟼ侧Ⰻ⡤ך姻׃ׁ׾➂꟦ָ ⥂鏾ׅ׸ל0, ˟勴⟝㘗זו׾ꟼ侧ך㘗ח⢪ֲ㜥さ׮㣐⡤ֿ׸חז׷勴⟝㘗כ䱿锷דֹזְךד
  36. תה׭ 䧮ղך顑⟣׾剑㼭ח׃אא㸜Ⰻ䚍׾䖤׷׋׭חכ ㎳ח״׷寅厩眔㔲׾杞ֻׅ׷ ׉׮׉׮㎳כז׷ץֻ鼘ֽ׷

  37. 'VSUIFS3FBEJOH –侁⻌罏ך5ZQF4DSJQUˊ 2JJUB CZ!VIZP –㸜Ⰻ䚍ך嚊䙀׾䓼ְ鎉衝ד铡ֻ鎸✲ –BOZװBT⟃㢩ך⽬ꤹ䚍׮《׶䪔׏גְתׅ –׀㸜䗰ֻ׌ְׁծֿךز٦ؙךⰻ㺁׾㸚׸ל⹧ⵃ罏דׅ – 5ZQF4DSJQUךABTTFSUTYJT5A㘗כוך״ֲח⽬ꤹזךַ ˊ

    2JJUB CZ!VIZP –顑⟣眔㔲׾杞ֻׅ׷הְֲ罋ִ倯׾ك٦أחِ٦ؠ٦㹀纏㘗鶢铂 ך⢪ְ倯׾罋㻊׃׋鎸✲