Programming TypeScript ch3 (2/2)
- Intermission: Type Aliases, Unions, and Intersections
- Arrays
- Tuples
- null, undefined, void, and never
- Enums
- Summary
- Exercises
- 英語
Intermission: Type Aliases, Unions, and Intersections
- 値に対して操作ができる
- number に対する
+
- string に対する
.toUpperCase()
- number に対する
- 型に対しても操作ができる
Type aliases
type Age = number type Person = { name: string age: Age } // { name: string; age: number; } let age: Age = 55 // number let driver: Person = { name: 'John', age: age } // Person
type Color = 'red' let x = Math.random() < 0.5 if (x) { type Color = 'blue' // 上のColor定義を隠す let b: Color = 'blue' } else { let c: Color = 'red' }
Union and Intersection Types
type Foo = { foo: boolean baz: boolean } type Bar = { bar: boolean baz: boolean } type FooOrBarOrBoth = Foo | Bar; type FooAndBar = Foo & Bar; let a: FooOrBarOrBoth a = { foo: true, baz: true } a = { bar: true, baz: true } a = { foo: true, bar: true, baz: true } a = { baz: true } // Error TS2322 let b: FooAndBar b = { foo: true, bar: true, baz: true } b = { foo: true, baz: true } // Error TS2322 b = { bar: true, baz: true } // Error TS2322
|
は「どちらか片方」ではない- 両方の集合の要素であってもよい
- 共通部分がなく、どちらか片方であるようなもの: 「Discriminated Union Types」
- 【補】
FooAndBar
型に一度受けてからFoo
に入れるのはOK、オブジェクトリテラルをそのまま入れるのはエラー
let b: FooAndBar = { foo: true, bar: true, baz: true } let c: Foo c = b; // OK c = { foo: true, bar: true, baz: true } // Error TS2322: Type '{ foo: true; bar: boolean; baz: true; }' is not assignable to type 'Foo'.
- Union Typesのほうがよくお目にかかる
function or (a: string, b: number) { return a || b } // returns string|number
Arrays
let a = [1,2,3] // number[] let b = [] // any[] let c = [2,'a'] // (string | number)[] const d = [2,'a'] // (string | number)[] a.push('b') // Error TS2345: Argument of type '"b"' is not assignable to parameter of type 'number'. b.push('b') c.push('b')
- 要素型は混ぜないほうがいい
- オブジェクト同様、
const
にしても特に型が狭まったりしない
Tuples
- 明示的に型宣言する必要がある
- JSのシンタックス上、配列とタプルの区別がないため配列に推論されてしまう
let a: [number] = [1] a = [] // Error TS2741: Property '0' is missing in type '[]' but required in type '[number]'. let b: [string,string] = ['Martin', 'Fowler'] b = ['Robert', 'C', 'Martin'] // Error TS2322: Type '[string, string, string]' is not assignable to type '[string, string]'.
- オプショナル要素
type PairOrTriple = [number, number, number?] type PairOrTriple2 = [number,number]|[number, number, number] let a: PairOrTriple[] = [ [1], // Error TS2741: Property '1' is missing in type '[number]' but required in type 'PairOrTriple'. [1,2], [1,2,3], [1,2,3,4], // Error TS2322: Type '[number, number, number, number]' is not assignable to type 'PairOrTriple'. ] let b: PairOrTriple2[] = [ [1], // Error TS2322: Type '[number]' is not assignable to type 'PairOrTriple2'. [1,2], [1,2,3], [1,2,3,4], // Error TS2322: Type '[number, number, number, number]' is not assignable to type 'PairOrTriple2'. ]
- 可変長
type RestSample = [number, boolean, ...string[]] let restSample: RestSample[] = [ [1,true], [1,true, 'hoge'], [1,true, 'hoge', 'fuga'], ]
Read-only arrays and tuples
let as: readonly number[] = [1,2,3] as.push(4); // Error TS2339: Property 'push' does not exist on type 'readonly number[]'. as.reverse(); // Error TS2339: Property 'reverse' does not exist on type 'readonly number[]'. let bs = as.concat(4); // number[] bs.push(5); bs.reverse();
- 長い書き方
type A = readonly string[] type B = ReadonlyArray<string> type C = Readonly<string[]> type D = Readonly<Array<string>> type E = readonly [number, string]; type F = Readonly<[number, string]>
null, undefined, void, and never
- 【所感】言うほど似てない
// returns number | null function a(x: number) { return x < 10 ? x : null; } // returns undefined function b() { return undefined } // returns void function c() { console.log('hola') } // returns never function d() { throw TypeError('always error'); } // returns never function e() { while (true) { // do something } } function f(x: 1 | 2 | 3) { switch (x) { case 1: x; // 1 break; case 2: x; // 2 break; case 3: x; // 3 break; default: x; // never } }
- never: ボトム型
- 他のどの型にも代入可能
Column: strict null checking
- 昔のTSでは
null
がnever
以外のボトム型だった - すべてがnullableだったということ
- nullチェックしないと安全でない
- 怠ると実行時例外が送出されうる
Enums
enum Language { English, // 0 Spanish, // 1 Russian // 2 } enum Color { Red = '#c10000', Pink = 0xc10050, White = 255 } let a = Color.Red // Color let b = Color.Green // Error TS2339: Property 'Green' does not exist on type 'typeof Color'. let c = Language[0] // string let d = Language[3] // string (!!!)
"use strict"; var Language; (function (Language) { Language[Language["English"] = 0] = "English"; Language[Language["Spanish"] = 1] = "Spanish"; Language[Language["Russian"] = 2] = "Russian"; // 2 })(Language || (Language = {})); var Color; (function (Color) { Color["Red"] = "#c10000"; Color[Color["Pink"] = 12648528] = "Pink"; Color[Color["White"] = 255] = "White"; })(Color || (Color = {})); let a = Color.Red; // Color // let b = Color.Green // Error TS2339: Property 'Green' does not exist on type 'typeof Color'. let c = Language[0]; // string let d = Language[3]; // string (!!!) //# sourceMappingURL=index.js.map
- 危険な値でのアクセスを禁止するには、
const enum
を使う
const enum Language { English, // 0 Spanish, // 1 Russian // 2 } const enum Color { Red = '#c10000', Pink = 0xc10050, White = 255 } let a = Color.Red // Color let c = Language[0] // Error TS2476: A const enum member can only be accessed using a string literal. let d = Language[3] // Error TS2476: A const enum member can only be accessed using a string literal.
- 出力されるJSコード
- C++11でいうところの
constexpr
な感じですね
- C++11でいうところの
"use strict"; let a = "#c10000" /* Red */; // Color // let c = Language[0] // Error TS2476: A const enum member can only be accessed using a string literal. // let d = Language[3] // Error TS2476: A const enum member can only be accessed using a string literal. //# sourceMappingURL=index.js.map
const enum
の利用
const enum Language { English, Spanish, Russian } function greet(lang: Language) { // stub return 'hello'; } greet(Language.English) greet(Language.Spanish) greet(Language.Russian) greet(99) // コンパイルが通ってしまう
- 出力されるJSコード
"use strict"; function greet(lang) { // stub return 'hello'; } greet(0 /* English */); greet(1 /* Spanish */); greet(2 /* Russian */); greet(99); // コンパイルが通ってしまう //# sourceMappingURL=index.js.map
- 値がnumberの
const enum
型には任意のnumberを代入できてしまう… - 回避するには、値を文字列にする
const enum Language { English = 'English', Spanish = 'Spanish', Russian = 'Russian' } function greet(lang: Language) { // stub return 'hello'; } greet(Language.English) greet(Language.Spanish) greet(Language.Russian) greet('Deutsch') // Error TS2345: Argument of type '"Deutsch"' is not assignable to parameter of type 'Language'.
- TSのenumは罠がいっぱいなので使わないこと推奨
- TSでは他にもっとよい表現方法がいろいろある
- 頑なにnumeric-valueなenumを使う同僚がいたら、non-const enumやnumeric valueに警告を出すTSLintルールをこっそりマージ(ninja-merge)しよう
Column: TCS Flag: preserveConstEnums
- tsconfigで
preserveConstEnums
をtrue
にすると、const enum
でもオブジェクトを生成するようになる
tsconfig.json
{ "compilerOptions": { ... "preserveConstEnums": true }, ... }
"use strict"; var Language; (function (Language) { Language[Language["English"] = 0] = "English"; Language[Language["Spanish"] = 1] = "Spanish"; Language[Language["Russian"] = 2] = "Russian"; // 2 })(Language || (Language = {})); var Color; (function (Color) { Color["Red"] = "#c10000"; Color[Color["Pink"] = 12648528] = "Pink"; Color[Color["White"] = 255] = "White"; })(Color || (Color = {})); let a = "#c10000" /* Red */; // Color // let c = Language[0] // Error TS2476: A const enum member can only be accessed using a string literal. // let d = Language[3] // Error TS2476: A const enum member can only be accessed using a string literal. //# sourceMappingURL=index.js.map
Summary
- TSには型がいっぱい
const
だとよりspecificな型が推論される
Type | Subtype |
---|---|
boolean | Boolean literal |
bigint | Bigint literal |
number | Number literal |
string | String literal |
symbol | unique symbol |
object | Object literal |
Array | Tuple |
enum | const enum |
Exercises
let h = null // any (!!!)
英語
- intermission
- 休憩
- grizzled
- 白髪まじりの灰色の頭をした
- グリズリー(ハイイログマ)
- 白髪まじりの灰色の頭をした