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 (!!!)
- C言語のenumと同じ感じに自動採番される
- 値でのアクセス(reverse-lookup)は危険
- 【補】出力されるJSコード
"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
- 白髪まじりの灰色の頭をした
- グリズリー(ハイイログマ)