型を見る会
Brand
@see1 https://medium.com/@KevinBGreene/surviving-the-typescript-ecosystem-branding-and-type-tagging-6cf6e516523d
@see2 https://zenn.dev/f_subal/articles/phantom_type_in_typescript
型に { hoge: unknown }みたく何かしら(大体文字列)生やして独自に区別するような 悪用 活用ができる
いわゆる Phontom Type っぽいものらしい 👻
こういうのもある
https://www.kabuku.co.jp/developers/typescript-で-radian-と-degree-を間違えないようにする
code:foo.ts
// @see1 の記事のやつ
type Brand<K, T> = K & { __brand: T }
type USD = Brand<number, 'USD'>
type EUR = Brand<number, 'EUR'>
const usd = 10 as USD
const eur = 10 as EUR
function euroToUsd(euro: EUR): USD {
return (euro * 1.18 as USD)
}
console.log(USD: ${euroToUsd(eur)})
// -----------
// こっちのほうがまだ安全でいいですね
type Brand<T, U extends string> = T & { key in U: never }
type Meat = 'Beaf' | 'Chicken'
type FoodPrice = Brand<number, Meat>
// うーん
const meat = (n: number) => n as FoodPrice
const logMeatPrice = (price: FoodPrice) => console.log(price)
logMeatPrice(meat(120)) // OK
logMeatPrice(120) // Error
ユーザー定義の型ガード
@see https://typescript-jp.gitbook.io/deep-dive/type-system/typeguard#yznotype-guard
Brand と合わせると例えば File の拡張子判別とかが型レベルに出来る
code:foo.ts
type Wav = Brand<string, 'wav'>
type Mp3 = Brand<string, 'mp3'>
const wavRegexp = RegExp(/\.wav$/i)
const mp3Regexp = RegExp(/\.mp3$/i)
const isWav = (arg: string): arg is Wav => wavRegexp.test(arg)
const isMp3 = (arg: string): arg is Mp3 => mp3Regexp.test(arg)
const fileName = 'foo.wav' as Wav | Mp3 // ここ本当は upload されたやつ
if (isWav(fileName)) {
console.log(fileName) // Wav型
} else {
console.log(fileName) // Mp3型
}
as に近い存在なので乱用はやめといたほうがいいが、上のような場面はこうするしかなさそう
interface は instanceOf が使えないが、ユーザー定義の型ガードで型の確認が代用出来る
https://qiita.com/suin/items/0ce77f31cbaa14031288#interfaceのタイプガードはどうやったらいいか
他にもメールアドレスのバリデーションを型レベルに出来る(普通にバリデーションやれって話かもしれないが)
code:validate-email.ts
export type Brand<T, U extends string> = T & { P in U: unknown }
export type Email = Brand<string, 'email'>
// @see https://www.w3resource.com/javascript/form/email-validation.php
const isValidEmail = (arg: string): arg is Email => /^a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-+@a-zA-Z0-9-+(?:\.a-zA-Z0-9-+)*$/.test(arg)
if (isValidEmail(input)) console.log(input) // => Email型
Flow の $Diff
@see https://qiita.com/Quramy/items/b45711789605ef9f96de
TypeScript だと Conditional Types で出来る
普通に Pick と Excludeで出来る
code:foo.ts
// これで T(union) から U を落とした union が取れる
// 一応 Diff
type Diff<T, U> = T extends U ? never : T;
type RequiredKeys = Diff<"foo" | "bar" | "piyo", "foo"> // "bar" | "piyo"
// Flow の $Diff
// オブジェクトの型突っ込んで Diff したのが取れる
type $Diff<T, U> = { P in Diff<keyof T, keyof U>: TP }
// ----------
// Pick 使って書けるね
type $Diff<T, U> = Pick<T, Diff<keyof T, keyof U>>
type Props = { name: string, age: number }
type DefaultProps = { age: number }
type DiffedProps = $Diff<Props, DefaultProps> // { name: string } が取れる
// ----------
// Exclude でやれます
type $Diff<T, U> = Pick<T, Exclude<keyof T, keyof U>>
型でこういうの表現したいけどどうしよ〜ってときに見るもの
https://github.com/krzkaczor/ts-essentials
これ使って無理なら頭使う
タイピングしてぇ〜ってときに見るもの
https://github.com/type-challenges/type-challenges
寿司打に飽きたらどうぞ
中級ぐらいで気持ちがバキバキに折れた
PR 出すと採点までしてくれるらしい
お得〜!!おれはやりませんが...
TypeScript 型システム 健全性
なぜ TypeScript の型システムが健全性を諦めているか
TypeScript でイキリ始めた人間がいたらとりあえずこれ投げとけ感がある
全ての黒幕
↑の記事にもあるが、TypeScript の Array 型は 共変
しかし TypeScript の関数型の引数は 双変 扱いなのでコンパイルエラーにならないときがある
関数型の引数はふつう共変性はない
オッってなる
Promise の then の callback でこいつに噛まれたりする
元の記事見て〜
これをやめろ〜!って動きがあるらしい
エンドユーザー殲滅作戦
TypeScript最終回
Deep Dive にも双変性の解説がある