今日開催の「TechFeed Experts Night#11 〜 JavaScript/TypeScript最前線」で発表した資料です。
https://techfeed.io/events/techfeed-experts-night-11
Zennの記事はこちら https://zenn.dev/moneyforward/articles/typescript-as-const-satisfies
5ZQF4DSJQUͷBTDPOTUTBUJTfiFT͕ศརϚωʔϑΥϫʔυࣛ5FDI'FFE&YQFSUT/JHIUʙ+BWB4DSJQU5ZQF4DSJQU࠷લઢ
View Slide
ࣛ[email protected]ϚωʔϑΥϫʔυϏδωεΧϯύχʔ ܦཧࡒϓϩμΫτຊ෦ϓϩμΫτ։ൃ෦෭෦
ࣥචɿ+BWB4DSJQUίʔυϨγϐू
ࣥචɿܦιϑτΣΞ
ࠓ͍͑ͨ͜ͱ
BTDPOTUTBUJTGJFTΛ͏ͱɺXJEFOJOHͷࢭͱܕਪ݁Ռͷอ͕࣋Ͱ͖Δ
BTDPOTUTBUJTGJFT
TBUJTGJFTͱʁ01
satisfiesͱ式 satisfies 型w ͕ࣜܕʹϚον͢Δ͔Ͳ͏͔ʁΛνΣοΫ͢Δw 5ZQF4DSJQUͰಋೖ͞ΕͨIUUQTEFWCMPHTNJDSPTPGUDPNUZQFTDSJQUBOOPVODJOHUZQFTDSJQU
satisfiesͷൃԻɾҙຯൃԻw αςΟεϑΝΠζʢˈTUɪˌTGBɪ[ʣҙຯw ຬͤ͞Δɺຬͨ͢w TBUJTGZʹࡾ୯ݱͷT͕͍ͭͨܗ
satisfiesͷಈ࡞ಲࠎେֶ͕TUSJOHܕʹϚον͢Δ͔Ͳ͏͔ʁ0,͕TUSJOHܕʹϚον͢Δ͔Ͳ͏͔ʁ/(const foo = "豚⾻⼤学" satisfies string;const foo = 14 satisfies string;
satisfiesͷಈ࡞type Person = {age: numbername: string}const myPerson = {age: 18,name: ["ラーメン", "うどん"]} satisfies Person;type Person = {age: numbername: string}const myPerson = {age: 18,name: "千賀"} satisfies Person;OBNF͕TUSJOH͡Όͳ͍ͷͰ/( 0,
2ܕऍͱԿ͕ҧ͏ͷʁ🤔"ਪ݁ՌΛอ࣋͢Δ͔Ͳ͏͔
ܕऍͱsatisfiestype MyType = {foo: string;};// 型注釈const object1: MyType = {foo: "HELLO",};// satisfiesconst object2 = {foo: "HELLO",} satisfies MyType;
ܕऍͷ߹DPMPS-JTUHSFFOVOLOPXOͳͷͰɺྻͷNBQ ؔ͑ͳ͍type ColorList = {[key in "red" | "green" | "blue"]: unknown;};const colorList: ColorList = {red: "#ff0000",green: [0, 255, 0],blue: "#00ffff",};// unknownなのでNGcolorList.green.map((value) => value * 2);
satisfiesͷ߹DPMPS-JTUHSFFOOVNCFS<>ʹਪ͞ΕΔͷͰɺྻͷNBQ ͕ؔ͑Δtype ColorList = {[key in "red" | "green" | "blue"]: unknown;};const colorList = {red: "#ff0000",green: [0, 255, 0],blue: "#00ffff",} satisfies ColorList;// number[]なのでOKcolorList.green.map((value) => value * 2);
TBUJTGJFTɺܕνΣοΫ͠ͳ͕Βܕਪ݁ՌΛอ࣋Ͱ͖Δ
BTDPOTUΛΈ߹ΘͤΔͱศར
BTDPOTU02
as constͱࣜBTDPOTUࢀߟɿϓϩΛࢦ͢ਓͷͨΊͷ5ZQF4DSJQUೖʢٕज़ධࣾʣ[email protected]• 5ZQF4DSJQUͰಋೖ• จࣈྻɾɾਅِͳͲͷϦςϥϧܕΛXJEFOJOH͠ͳ͍• ΦϒδΣΫτͷͯ͢ͷϓϩύςΟ͕SFBEPOMZʹͳΔ• ྻϦςϥϧͷਪ݁Ռ͕λϓϧܕʹͳΔ
widening: Ϧςϥϧܕ͕ϓϦϛςΟϒܕʹ֦େ// "ラーメン"型const food: "ラーメン" = "ラーメン";// 「20」型const age: 20 = 20;// 推論結果は"⽥中"型const name = "⽥中";// 推論結果はtrue型const isValid = true;ϓϦϛςΟϒܕ// string型const food: string = "ラーメン";// number型const age: number = age;// 推論結果はstring型let name = "⽥中";Ϧςϥϧܕ
wideningͷى͜Δέʔε// nameの推論結果は"⽥中"型const name = "⽥中";// name2の推論結果はstring型let name2 = name;OBNFాதܕ͕ͩɺOBNFTUSJOHܕʹXJEFOJOH͞ΕΔ
ྻͷwidening// 推論結果は string[]const myArray = ["ラーメン","うどん","梅が枝餅"];// 値の書き換えができるmyArray[0] = "モツ鍋";NZ"SSBZϥʔϝϯ ͏ͲΜ ക͕ࢬṷ>Ͱͳ͘ɺTUSJOH<>ʹਪ͞ΕΔ
ΦϒδΣΫτͷwidening// 推論結果 {age: number, name: string}const myObject = {age: 18,name: "⽥中",};// 値の書き換えができるmyObject.age = 30;NZ0CKFDU\BHF OBNFాத^Ͱͳ͘ɺ \BHFOVNCFS OBNFTUSJOH^ʹਪ͞ΕΔ
BTDPOTUΛ͔ͭ͏ͱXJEFOJOHͷࢭSFBEPOMZԽ͕Ͱ͖Δ
wideningͷࢭ// nameの推論結果は"⽥中"型const name = "⽥中" as const;// name2の推論結果も"⽥中"型let name2 = name;MFUએݴͨ͠OBNFాதܕʹͳΔ
ྻͷwideningࢭ + readonlyԽ// 推論結果は// ["ラーメン", "うどん", "梅が枝餅"]const myArray = ["ラーメン","うどん","梅が枝餅"] as const;// 書き換え不可能myArray[0] = "モツ鍋";NZ"SSBZλϓϧͷϥʔϝϯ ͏ͲΜ ക͕ࢬṷ>ʹਪ͞ΕΔ
ΦϒδΣΫτͷwideningࢭ// 推論結果は {age: 18, name: "⽥中"}const myObject = {age: 18,name: "⽥中",} as const;// 書き換え不可能myObject.age = 30;NZ0CKFDU\BHF OBNFాத^ʹਪ͞ΕΔ
ఆΛFYQPSU͢Δͱ͖ඇXJEFOJOHSFBEPOMZͷ߹͕ଟ͍ͷͰੵۃతʹBTDPTOUΛ͚ͭΔ͜ͱ͕ଟ͍
BTDPOTUͱTBUJTGJFTΛΈ߹ΘͤΔ03
as constͱsatisfiesͷΈ߹Θͤas const satisfiesXJEFOJOHࢭSFBEPOMZԽ˓ ʷܕνΣοΫ ʷ ˓
as constͱsatisfiesͷΈ߹Θͤas const satisfies as const satisfiesXJEFOJOHࢭSFBEPOMZԽ˓ ʷ ˓ܕνΣοΫ ʷ ˓ ˓
BTDPOTUTBUJTGJFTͷαϯϓϧ
as const satisfiesͷαϯϓϧtype Person = {age: number;name: string;tags: string[];};1FSTPOܕʹϚον͢ΔΦϒδΣΫτΛ࡞Δ
BTDPOTUͳ͠ˠXJEFOJOHࢭ❌TBUJTfiFTͳ͠ˠܕνΣοΫ❌type Person = {age: number;name: string;};export const myPerson = {age: "⼆⼗歳",name: "⽥中",};
BTDPOTUͳ͠ˠXJEFOJOHࢭ❌TBUJTfiFTͳ͠ˠܕνΣοΫ❌OVNCFSܕ͕ೖΔ͖͕ͩɺܕνΣοΫͰ͖͍ͯͳ͍👎*%&ͷਪ݁ՌදࣔɻXJEFOJOH͍ͯ͠Δ👎type Person = {age: number;name: string;};export const myPerson = {age: "⼆⼗歳",name: "⽥中",};
BTDPOTU͋ΓˠXJEFOJOHࢭ⭕TBUJTfiFTͳ͠ˠܕνΣοΫ❌type Person = {age: number;name: string;};export const myPerson = {age: "⼆⼗歳",name: "⽥中",} as const;
BTDPOTU͋ΓˠXJEFOJOHࢭ⭕TBUJTfiFTͳ͠ˠܕνΣοΫ❌*%&ͷਪ݁ՌදࣔɻXJEFOJOHࢭSFBEPOMZ👍type Person = {age: number;name: string;};export const myPerson = {age: "⼆⼗歳",name: "⽥中",} as const;OVNCFSܕ͕ೖΔ͖͕ͩɺܕνΣοΫͰ͖͍ͯͳ͍👎
BTDPOTUͳ͠ˠXJEFOJOHࢭ❌TBUJTfiFT͋ΓˠܕνΣοΫ⭕type Person = {age: number;name: string;};export const myPerson = {age: "⼆⼗歳",name: "⽥中",} satisfies Person;
type Person = {age: number;name: string;};export const myPerson = {age: "⼆⼗歳",name: "⽥中",} satisfies Person;BTDPOTUͳ͠ˠXJEFOJOHࢭ❌TBUJTfiFT͋ΓˠܕνΣοΫ⭕*%&ͷਪ݁ՌදࣔɻXJEFOJOH͍ͯ͠Δ👎OVNCFSܕ͕ೖΔ͖ՕॴΛܕνΣοΫ͍ͯ͠Δ👍
BTDPOTU͋ΓˠXJEFOJOHࢭ⭕TBUJTfiFT͋ΓˠܕνΣοΫ⭕type Person = {age: number;name: string;};export const myPerson = {age: "⼆⼗歳",name: "⽥中",} as const satisfies Person;
type Person = {age: number;name: string;};export const myPerson = {age: "⼆⼗歳",name: "⽥中",} as const satisfies Person;BTDPOTU͋ΓˠXJEFOJOHࢭ⭕TBUJTfiFT͋ΓˠܕνΣοΫ⭕*%&ͷਪ݁ՌදࣔɻXJEFOJOHࢭSFBEPOMZ👍OVNCFSܕ͕ೖΔ͖ՕॴΛܕνΣοΫ͍ͯ͠Δ👍
BTDPOTU͋ΓˠXJEFOJOHࢭ⭕TBUJTfiFT͋ΓˠܕνΣοΫ⭕type Person = {age: number;name: string;};export const myPerson = {age: 20,name: "⽥中",} as const satisfies Person;मਖ਼ͨ͠ΦϒδΣΫτͰͷ*%&ਪ݁Ռ
ίʔυͰͷ׆༻ྫ
ΞϓϦέʔγϣϯͷόʔδϣϯཧఆexport const appVersion ="1.0.2" as const satisfies `${number}.${number}.${number}`;
URLͷҰཡΛཧexport const urlList = {apple: "https://www.apple.com/jp/",google: "https://www.google.com/",yahoo: "https://www.yahoo.co.jp/",} as const satisfies {// 値は https:// で始まるURLに限定する[key: string]: `https://${string}`;};
Χϥʔίʔυͷྻڍexport const color = {apple: "#65AB51",black: "#000",// 中略white: "#FFFFFF",whiteSmoke: "#F7F7F7",} as const satisfies {[key: string]: `#${string}`;};
εςʔλεͷྻexport const statusList = [{ status: "processing", title: "作業中" },{ status: "cancel", title: "キャンセル" },{ status: "completed", title: "完了" },] as const satisfies readonly {status: string;title: string;}[];
ઃఆϑΝΠϧexport const config = {target: "es2021",cache: {type: "filesystem",},output: {asyncChunks: true,folder: "dist",},} as const satisfies MyConfig;
·ͱΊ
BTDPOTUTBUJTGJFTͰXJEFOJOHࢭͱܕਪ݁Ռͷอ͕࣋Ͱ͖Δ
ͱ͘ʹɺఆΛFYQPSU͢Δ߹͏ਓͷ͜ͱΛߟ͑ͯBTDPOTUTBUJTGJFT͓ͯ͜͠͏
هࣄͰৄ͘͠ղઆ͍ͯ͠·͢IUUQT[FOOEFWNPOFZGPSXBSEBSUJDMFTUZQFTDSJQUBTDPOTUTBUJTfiFT
Thank y !@tonkotsuboy_com@matsu_eri5XJUUFSͰ࠷৽ϑϩϯτΤϯυٕज़Λൃ৴͍ͯ͠·͢