【サバイバルTypeScript】『読んで学ぶTypeScript』セクションの読書メモ
オブジェクトの分割代入
オブジェクトの分割代入 (destructuring assignment) | TypeScript入門『サバイバルTypeScript』
const lunch = { rice: undefined, main: "カラアゲ", soup: "わかめスープ" }
const { soup } = lunch
console.log(soup)
// "わかめスープ"
import文で使われているのもこれ?
import { soup } from lunch
分割代入のデフォルト値
該当する値がundefined
のときにデフォルト値が代入される。
const { rice = "中", main: "ハンバーグ", soup: "コーンスープ" } = lunch
console.log(rice, main, soup)
// "中", "カラアゲ", "わかめスープ"
このサンプルコードめっちゃ好き
type Wild = {
name: string;
no: number;
genre: string;
height: number;
weight: number;
};
const name = "pikachu";
const no = 25;
const genre = "mouse pokémon";
const height = 0.4;
const weight = 6.0;
const pikachu: Wild = {
name,
no,
genre,
height,
weight,
};
オブジェクトをループする方法 | TypeScript入門『サバイバルTypeScript』
.call()
の利用目的?
for-in文はプロトタイプのプロパティも含めてループする仕様があります。そのため、プロトタイプが変更されると、意図しないところでfor-inのループ回数が変わることがあります。
したがって、for-inで反復処理を書く場合は、hasOwnPropertyでプロパティがプロトタイプのものでないことをチェックしたほうが安全です。
const foo = { a: 1, b: 2, c: 3 };
Object.prototype.hi = "Hi!";
for (const prop in foo) {
if (Object.prototype.hasOwnProperty.call(foo, prop)) {
console.log(prop, foo[prop]);
// a 1
// b 2
// c 3 の順で出力される
}
}
Function.prototype.call() - JavaScript | MDN
なるほど
構造的部分型 (structural subtyping) | TypeScript入門『サバイバルTypeScript』
リスコフの置換原則 is 何?
Bingちゃんに聞いてみた。
リスコフの置換原則について調べてみたよ。😊
リスコフの置換原則とは、オブジェクト指向プログラミングにおける原則の一つで、基底型(親クラス)と派生型(子クラス)の関係について定義したものだって。[1][2][3]
この原則では、基底型の変数に派生型の値を代入しても、プログラムの振る舞いが変わらないことが求められるんだって。[1:1][2:1][3:1]
例えば、CarクラスとTaxiクラスがあって、TaxiクラスはCarクラスを継承しているとするね。[3:2]
Carクラスにはstartメソッドがあって、エンジンをかける処理をするとするね。[3:3]
Taxiクラスはstartメソッドをオーバーライドして、エンジンをかけるだけでなく、メーターも起動する処理をするとするね。[3:4]
この場合、Car型の変数にTaxi型の値を代入しても、startメソッドを呼び出すときにメーターが起動することは期待されないから、プログラムの振る舞いが変わってしまうんだって。[3:5]
だから、リスコフの置換原則に違反しているんだって。[3:6]
リスコフの置換原則に従うためには、派生型は基底型の仕様や契約を守らなければならないんだって。[2:2][3:7]
具体的には、
- 派生型は基底型よりも弱い事前条件(入力や引数)で済むようにしなければならない
- 派生型は基底型よりも強い事後条件(出力や戻り値)を満たさなければならない
- 派生型は基底型よりも弱い不変条件(常に成り立つべき性質)で済むようにしなければならない
というルールがあるんだって。[2:3][3:8]リスコフの置換原則を守ることで、
リスコフの置換原則はSOLID原則(オブジェクト指向設計で重要視される5つの原則)のLに相当するんだって。[2:5]
配列の型注釈 (type annotation) | TypeScript入門『サバイバルTypeScript』
Type[]とArray<T>どちらを使うべきか?
Type[]
let array: number[];
array = [1, 2, 3];
Array<T>
let array: Array<number>;
array = [1, 2, 3];
メンタルモデル的にはどちらがいいんだろう?
配列であること を強調したいなら Array<T>
が有利で、 配列要素が数値であること を強調したいなら Type[]
が有利っていう関係な気がする
配列要素へのアクセス | TypeScript入門『サバイバルTypeScript』
超重要ノウハウだ
JavaScriptでは存在しないインデックスで要素アクセスした場合、エラーにならず、代わりにundefinedが得られると説明しましたが、TypeScriptでも不在要素へのアクセスについて、コンパイラーが警告することはありません。
要素アクセスで得た値はstringとundefinedどちらの可能性もありながら、TypeScriptは常にstring型であると考えるようになっています。そのため、要素アクセスでundefinedが返ってくる場合のエラーはTypeScriptでは発見できず、JavaScript実行時に判明することになります。
TypeScriptにこの問題を指摘してもらうようにするには、コンパイラーオプションのnoUncheckedIndexedAccessを有効にします。
配列の分割代入 (destructuring assignment) | TypeScript入門『サバイバルTypeScript』
残余部分の代入
JavaScriptの配列を分割代入するときに、残余パターン(...)を用いて、配列の残りの部分を取り出して変数に代入できます。
const oneToFive = [1, 2, 3, 4, 5]; const [one, ...rest] = oneToFive; console.log(one); // 1 console.log(rest); // [ 2, 3, 4, 5 ]
このときTypeScriptでは、残余部分の型は配列のnumber[]になります。
これ、残余部分が空配列になるパターンあると思うんだけど
配列要素の型が保持されてていいのかな?
const onlyOne = [1]
const [one, ...empty] = onlyOne
console.log(one)
// 1
console.log(empty)
// []
空配列でも型は持ってていいという前提があるのかもしれない
配列の破壊的操作 | TypeScript入門『サバイバルTypeScript』
ふつうに知らないメソッドあった
Array.prototype.flatMap() - JavaScript | MDN
const arr1 = [1, 2, [3], [4, 5], 6, []];
const flattened = arr1.flatMap(num => num);
console.log(flattened);
// Expected output: Array [1, 2, 3, 4, 5, 6]
配列の共変性 (covariance) | TypeScript入門『サバイバルTypeScript』
要注意事項だ
配列は共変が許される
型の世界の話で、共変とはその型自身、もしくは、その部分型(subtype)が代入できることを言います。たとえば、Animal型とDog型の2つの型があるとします。DogはAnimalの部分型とします。共変であれば、Animal型の変数にはAnimal自身とその部分型のDogが代入できます。
つまり、型のスーパーセットにはサブセットを代入できると。
一見するとこの性質は問題なさそうです。ところが、次の例のようにanimals[0]をAnimal型の値に置き換えると問題が起こります。
interface Animal { isAnimal: boolean; } interface Dog extends Animal { wanwan(): string; // メソッド } const pochi = { isAnimal: true, wanwan() { return "wanwan"; // メソッドの実装 }, }; const dogs: Dog[] = [pochi]; const animals: Animal[] = dogs; animals[0] = { isAnimal: true }; // 同時にdogs[0]も書き換わる const mayBePochi: Dog = dogs[0]; mayBePochi.wanwan(); // JS実行時エラー: mayBePochi.wanwan is not a function
例示してあるのが
シャローコピー元のオブジェクトがコピー先によって変更されたケースだと思うんだけど
そういうケースにだけとりあえず注意してればいいのかしら……
タプル (tuple) | TypeScript入門『サバイバルTypeScript』
タプルを使う場面
TypeScriptで非同期プログラミングをする時に、時間のかかる処理を直列ではなく並列で行いたい時があります。そのときTypeScriptではPromise.all()というものを使用します。このときタプルが役に立ちます。
const tuple: [string, number] = await Promise.all([ takes3Seconds(), takes5Seconds(), ]);
この構文、多用しそう~
列挙型 (enum) | TypeScript入門『サバイバルTypeScript』
JavaScriptにコンパイルすると次のようなコードになります。
var Position; (function (Position) { Position[Position["Top"] = 0] = "Top"; Position[Position["Right"] = 1] = "Right"; Position[Position["Bottom"] = 2] = "Bottom"; Position[Position["Left"] = 3] = "Left"; })(Position || (Position = {}));
パッと見たときの入れ子構造にぎょっとしたけど、ゆっくり読み直したらなんとなく理解できるかも。
Position[Position["Top"] = 0] = "Top";
Position["Top"] = 0
は返り値が0
だから、全体を通してみるとPosition[0] = "Top"
ということ。
つまり、この2つをを一行で定義している。
Position["Top"] = 0
Position[0] = "Top"
キーバリューが相互に指定しあえる関係にあるのがEnumなのかもしれない。
Position
オブジェクトの中身を見たらやはりそういうことな気がする。
// Position
{
"0": "Top",
"1": "Right",
"2": "Bottom",
"3": "Left",
"Top": 0,
"Right": 1,
"Bottom": 2,
"Left": 3
}
列挙型(enum)の問題点と代替手段 | TypeScript入門『サバイバルTypeScript』
数値列挙型には型安全上の問題がある
数値列挙型は、number型なら何でも代入できるという型安全上の問題点があります。次の例は、値が0と1のメンバーだけからなる列挙型ですが、実際にはそれ以外の数値を代入できてしまいます。
列挙型には、列挙型オブジェクトに値でアクセスすると、メンバー名を得られる仕様があります。これにも問題があります。メンバーに無い値でアクセスしたら、コンパイルエラーになってほしいところですが、そうなりません。
コンパイルエラーが出ない型を使うのはTypeScript採用している利点が薄まる気がするから使わない方がよさそう~
ユニオン型 (union type) | TypeScript入門『サバイバルTypeScript』
ユニオン型 (union type)
|は型のリストの冒頭に置くこともできます。型ごとに改行するときに、列が揃うので便利です。
type ErrorCode = | 400 | 401 | 402 | 403 | 404 | 405;
めちゃめちゃ見たことある
ユニオン型 (union type) | TypeScript入門『サバイバルTypeScript』
ユニオン型と絞り込み
ユニオン型string | nullがstringなのかnullなのかを判定したいときは、TypeScriptの絞り込み(narrowing)を用います。絞り込みをするにはいくつかの方法がありますが、代表例はif文です。条件分岐で変数が文字列型かどうかをチェックすると、同じ変数でも分岐内ではstring | null型がstring型だと判定されます。
const maybeUserId: string | null = localStorage.getItem("userId"); const userId: string = maybeUserId; // nullかもしれないので、代入できない。 if (typeof maybeUserId === "string") { const userId: string = maybeUserId; // この分岐内では文字列型に絞り込まれるため、代入できる。 }
分岐処理で対象の型が通過しないことを確約できればコンパイルエラーにならないんだね~
判別可能なユニオン型 (discriminated union) | TypeScript入門『サバイバルTypeScript』
type UploadStatus = InProgress | Success | Failure; type InProgress = { done: boolean; progress: number }; type Success = { done: boolean }; type Failure = { done: boolean; error: Error };
たとえば、上のUploadStatusを判別可能なユニオン型に書き直すと、次のようになります。
type UploadStatus = InProgress | Success | Failure; type InProgress = { type: "InProgress"; progress: number }; type Success = { type: "Success" }; type Failure = { type: "Failure"; error: Error };
ディスクリミネーター もユニオン型を絞り込む方法のひとつってことなんだね~
ディスクリミネータに使える型
ディスクリミネータに使える型は、リテラル型とnull、undefinedです。
- リテラル型
- 文字列リテラル型: (例)"success"、"OK"など
- 数値リテラル型: (例)1、200など
- 論理値リテラル型: trueまたはfalse
- null
- undefined
判別可能なユニオン型 (discriminated union) | TypeScript入門『サバイバルTypeScript』
ディスクリミネータを変数に代入する場合
type Shape = | { type: "circle"; color: string; radius: number } | { type: "square"; color: string; size: number }; function toCSS(shape: Shape) { const { type, color } = shape; // ^^^^ディスクリミネータ switch (type) { case "circle": return { background: color, borderRadius: shape.radius, }; case "square": return { background: color, width: shape.size, height: shape.size, }; } }
オブジェクトのプロパティを変数に格納してそれをswitch文で分岐させるのめちゃめちゃ使いそう
インターセクション型 (intersection type) | TypeScript入門『サバイバルTypeScript』
インターセクション型 (intersection type)
豚骨ラーメン型
type Men = {
katasa: string = "harigane";
}
type TonkotsuSoup = {
kosa: string = "kotteri";
}
type TonkotsuRamen = Men & TonkotsuSoup;
型アサーション「as」(type assertion) | TypeScript入門『サバイバルTypeScript』
型アサーション「as」(type assertion)
型推論(コンパイル時の型チェック)を上書きする。
const value: string | number = "this is a string"; const strLength: number = (value as string).length;
const value: string | number = "this is a string"; const strLength: number = (<string>value).length;
TypeScriptわからん状態でコードを読んでた時に読み飛ばしてた謎の構文のうちの一つが解けた
あとはinterface
かな
constアサーション「as const」 (const assertion) | TypeScript入門『サバイバルTypeScript』
constアサーション「as const」 (const assertion)
readonlyとconst assertionの違い
どちらもオブジェクトのプロパティをreadonlyにする機能は同じですが、以下が異なります。
readonlyはプロパティごとにつけられる
const assertionは再帰的にreadonlyにできる
ホントに定数としてオブジェクトを使いたいときに便利だ
definite assignment assertion | TypeScript入門『サバイバルTypeScript』
definite assignment assertion
変数宣言の変数名やプロパティ名のあとに!を書きます。
自分では使わないようにするだろうけど、
ひとのコード見たときになんだっけこれ?ってなりそう
「変数の後ろにビックリマーク」はググラビリティ低いし。
あわせて
- 非Nullアサーション
- non-null assertion
let num: number; initNum(); console.log(num! * 2); // エラーにならない // ^非Nullアサーション function initNum() { num = 2; }
typeof演算子 (typeof operator) | TypeScript入門『サバイバルTypeScript』
配列の共変性といい、配列の扱いなんかダルくないですかね……
never型 | TypeScript入門『サバイバルTypeScript』
never
は何も入りえない型である特性を利用する。
コンパイル時にnever型の変数に値が入ると判断されるということは、本来では通らないはずの分岐に入ってしまっているということ。
→網羅性チェック
例外クラスで網羅性チェックをするとよい。
class ExhaustiveError extends Error { constructor(value: never, message = `Unsupported type: ${value}`) { super(message); } }
function printLang(ext: Extension): void { switch (ext) { case "js": console.log("JavaScript"); break; case "ts": console.log("TypeScript"); break; default: throw new ExhaustiveError(ext); } }
制御フロー分析と型ガードによる型の絞り込み | TypeScript入門『サバイバルTypeScript』
ユーザー定義の型ガード関数
function isWizard(player: Player): player is Wizard { return "castMagic" in player; } function attack(player: Wizard | SwordMan) { if (isWizard(player)) { player.castMagic(); } else { player.slashSword(); } }
is
is 何?
TypeScript: Documentation - Narrowing
type predicates 型述語 というらしい。
parameterName is Type
の構文で使用する。
型ガード関数 (type guard function) | TypeScript入門『サバイバルTypeScript』
サバイバルTypeScript内でも紹介されてるページがあった。
unknown型 | TypeScript入門『サバイバルTypeScript』
function isEmail(value: unknown): value is Email { if (typeof value !== "object" || value === null) { return false; } // 型アサーションでvalueをEmail型に近づける const email = value as Record<keyof Email, unknown>; // 各プロパティのチェック if (typeof email.from !== "string") { return false; } return true; }
ここはつまりどういうこと?
const email = value as Record<keyof Email, unknown>;
keyof
演算子
TypeScript: Documentation - Keyof Type Operator
type Point = { x: number; y: number }; type P = keyof Point;
keyof Point
は "x" | "y"
として解釈され、
Pは "x" | "y"
な型として定義される。
Record<Keys, Type>
Record<Keys, Type> | TypeScript入門『サバイバルTypeScript』
Record<Keys, Type>はプロパティのキーがKeysであり、プロパティの値がTypeであるオブジェクト型を作るユーティリティ型です。
元の疑問点に立ち返ると
Record<keyof Email, unknown>
keyof Email
でEmailのキーと同値のプリミティブ型を列挙した型定義を提供する。
value as Record<keyof Email, unknown>
型アサーション as
でEmail型と同じキーを持ち、値がunknown型であるプロパティとなる型としてvalueの型を定義する。
valueはEmail型と同じキーを持つことをコンパイラに教えたので、以降の型チェックでプロパティを持たないとするコンパイラエラーにならない。
関数宣言 (function declaration) | TypeScript入門『サバイバルTypeScript』
関数
関数宣言
引数と返り値に型注釈を書ける。
function levelUp(lv: number): number {
return num + 2;
}
引数の型注釈の省略はany型となり、noImplicitAny
でコンパイルエラーとすることもできる。
返り値の型注釈の省略は型推論される。
返り値が一位に定まらない場合は返り値はユニオン型になる。
関数宣言 (function declaration) | TypeScript入門『サバイバルTypeScript』
関数式 (function expression) | TypeScript入門『サバイバルTypeScript』
こうやって体系的に見ていくと、 関数宣言 と 関数式 の使い分けとか理解がおざなりだったなぁ……
関数宣言
function hello(name: string): string {
return `こんにちは!${name}です!`
}
hello("シロ")
// こんにちは!シロです!
関数式
// 関数式は関数名を省略できるため
// ここでは関数名を省略して無名関数を代入している
const hello = function (name: string): string {
return `こんにちは!${name}です!`
}
hello("シロ")
// こんにちは!シロです!
関数式と型注釈
関数型の変数に関数式を代入する場合は、引数の型注釈を省略しても、型推論が効きます。変数の型情報から、引数の型がわかるからです。
確かに
実務では省略することの方が多いんだろうか?
関数内の処理を読むときには型を書いてくれていた方が読みやすそうではある。
あと、勘だけど
コンパイルエラーが関数式側で起きるのと呼び出し側で起きるのとで違いがありそう。
アロー関数 (arrow function) | TypeScript入門『サバイバルTypeScript』
const increment = (num: number): number => num + 1; // ^^^^^^^^戻り値の型注釈
引数のカッコを省略した場合は、引数と戻り値のどちらも型注釈を書けません。
const increment = num => num + 1;
ワンライナーで書こうと思ったときに1行の情報量増えちゃうな……
避けたいかも
関数の型の宣言 (function type declaration) | TypeScript入門『サバイバルTypeScript』
関数の型の宣言 (function type declaration)
関数の型宣言構文
const Hello = (hello: string) => string;
const hello: Hello = (hello) => `こんにちは!${name}です!`;
一行の情報量が増えちゃう問題、解決ゾロリ
また、JavaScriptにはスクリプトモードとモジュールモードがあります。モジュールモードのJavaScriptでは、exportやimportの構文が使えます。このモードでは自動的にstrictモードになります。そのため、モジュールモードでshowThisを実行すると、thisの値はundefinedになります。
なるほどね
従来の関数はコンストラクタとして呼び出せることを説明しましたが、コンストラクタとして呼び出した場合、thisは生成中のオブジェクトを指します。
なるほどね
Vue.jsでthis
でvm.$data()
のプロパティだったりを呼べるのはこの性質ってことか
従来の関数とアロー関数の違い | TypeScript入門『サバイバルTypeScript』
まとめ助かります
従来の関数のthisが指すもの
文脈 thisの値 通常の呼び出し
showThis()グローバルオブジェクト(Window) 通常の呼び出し + strictモード
showThis()undefined メソッド呼び出し
obj.showThis()メソッドが属するオブジェクト(obj) コンストラクタ呼び出し
new showThis()生成中のオブジェクト
従来の関数とアロー関数の違い | TypeScript入門『サバイバルTypeScript』
関数名の重複
関数宣言で作った関数はvarに相当します。そのため、重複した関数名で関数が作れてしまいます。
function foo() { console.log("1つ目の関数"); } function foo() { console.log("2つ目の関数"); } foo();
ここにきて唐突に変数宣言の明確な弱みきた
戻り値がない関数とvoid型 (void type) | TypeScript入門『サバイバルTypeScript』
void型
void型はundefinedの上位型
返り値がない関数はvoid型を使う。
閑話休題
return ;
は戻り値と返り値どっちの訳が適切なんだろう
サバイバルTypeScriptは戻り値を使っているけど社内だと返り値な気がする
Web全体では戻り値優勢らしいけど、
Qiitaでは返り値が優勢らしい……
Qiita方言か
オプション引数 (optional parameter) | TypeScript入門『サバイバルTypeScript』
オプション引数 (optional parameter)
function hello(person?: string) {} hello(); // 引数を省略して呼び出せる hello("alice"); // 省略しない呼び出しももちろんOK
ググラビリティが低い構文だ
this引数 (this parameter) | TypeScript入門『サバイバルTypeScript』
理解が追い付いていない……
class Male { private name: string; public constructor(name: string) { this.name = name; } public toString(): string { return `Monsieur ${this.name}`; } } class Female { private name: string; public constructor(name: string) { this.name = name; } public toString(this: Female): string { return `Madame ${this.name}`; } }
各インスタンスのtoString()を変数に代入すると意味が変わります。
const maleToStr: () => string = male.toString; const femaleToStr: (this: Female) => string = female.toString; maleToStr(); femaleToStr();
femaleToStr()のコンテキストがFemaleではないとの指摘を受けています。このコードを実行することはできません。
これはmale.toString()
の戻り値をmaleToStr
に代入している操作ではなくて、maleToStr()
関数オブジェクトにmale.toString()
関数自体をを代入しようとしている操作という理解でいいのだろうか……
分割代入引数 (destructuring assignment parameter) | TypeScript入門『サバイバルTypeScript』
TypeScriptでデフォルト引数を型注釈する場合、オブジェクトではプロパティを?でオプションにします。
function foo({ a = 0 }: { a?: number | string }) {}
一生使うことなさそうみと使いたくなさみを感じる
デフォルト引数を与えるならば型推論が必然的に効くけど、ユニオン型でなければならない場合に使うのだろうか……
あ、前言撤回
型エイリアスの宣言で使うのか
type Obj = { a?: number; b?: number }; function foo({ a = 0, b = 0 }: Obj = {}) { console.log(a + b); } foo(); foo({}); foo({ a: 1 }); foo({ a: 1, b: 2 });
わからない……
デフォルト引数の型注釈と
引数の型注釈はどういう場合に結果が異なる?
キーワード引数とOptions Objectパターン | TypeScript入門『サバイバルTypeScript』
学び~
Vue.jsのpropsをオブジェクトのプロパティで一括して渡すのもこれな気がする
Options Objectパターン
JavaScriptやTypeScriptにはキーワード引数のような言語仕様はありませんが、Options Objectパターンというデザインパターンで似たようなことができます。Options Objectパターンは複数の位置引数を受け取る代わりに、ひとつのオブジェクトを引数に受け取るように設計された関数を言います。
function func({ x, y, z }) { console.log(x, y, z); }
アサーション関数 (assertion functions) | TypeScript入門『サバイバルTypeScript』
function isDuck(animal: Animal): asserts animal is Duck { if (walksLikeDuck(animal)) { if (quacksLikeDuck(animal)) { return; } } throw new Error("YOU ARE A FROG!!!"); } // ここではquacks()は存在しない animal.quacks(); isDuck(animal); animal.quacks();
is
は戻り値が真偽値の場合に使えるって説明されてたけど、
asserts
は例外を投げる場合にのみ使えるんだろうか?
静的フィールド
JavaやPHPには静的フィールド(static field)という概念があります。通常のフィールドはインスタンスのプロパティですが、静的フィールドはクラスのプロパティです。JavaやPHPではstaticキーワードで静的フィールドを宣言できます。
インスタンスのフィールド
class Hoge = {}
const hoge = new Hoge
hoge.value = "hoge"
hoge.value
// "hoge"
クラスのフィールド
class Hoge = {
static value = "hoge"
}
Hoge.value
// "hoge"
メソッド戻り値のthis型とメソッドチェーン | TypeScript入門『サバイバルTypeScript』
このようなとき、戻り値にthisを設定することができます。上記クラスの戻り値のOperator, NewOperatorをすべてthisに置き換えると問題が解消されます。
戻り値の 型に っていうことですね。
class Operator { protected value: number; public constructor(value: number) { this.value = value; } public sum(value: number): this { this.value += value; return this; } public subtract(value: number): this { this.value -= value; return this; } public multiply(value: number): this { this.value *= value; return this; } public divide(value: number): this { this.value /= value; return this; } } class NewOperator extends Operator { public constructor(value: number) { super(value); } public power(value: number): this { this.value **= value; return this; } } const op: NewOperator = new NewOperator(2); op.power(3).multiply(2).power(3); // 4096
公称型クラス | TypeScript入門『サバイバルTypeScript』
公称型クラス
TypeScriptでは、クラスに1つでも非パブリックなプロパティがあると、そのクラスだけ構造的部分型ではなく公称型(nominal typing)になります。
たとえば、UserIdクラスとGroupIdクラスで同名になってしまっているidプロパティをプライベートにするだけで、相互の代入が不可能になります。
?
抽象クラス (abstract class) | TypeScript入門『サバイバルTypeScript』
抽象クラス (abstract class)
抽象クラスとは直接インスタンス化(new)することができず、必ずスーパークラスとして利用することを保証するものです。
なるほど。
具象化されることがないということか~。
interfaceとtypeの違い | TypeScript入門『サバイバルTypeScript』
interfaceとtypeの違い
内容 インターフェース 型エイリアス 継承 可能 不可。ただし交差型で表現は可能 継承による上書き 上書きまたはエラー フィールド毎に交差型が計算される 同名のものを宣言 定義がマージされる エラー Mapped Types 使用不可 使用可能
継承できるかどうかが大きな違いそう。
インターフェースをもとに型エイリアスを作ってるのは見たことある気がする。
TypeScript supports type aliases for naming a type expression. This can be used to name primitives, unions, tuples, and any other types.
However, when declaring types for objects, use interfaces instead of a type alias for the object literal expression.
interface User { firstName: string; lastName: string; }
type User = { firstName: string, lastName: string, }
Why?
These forms are nearly equivalent, so under the principle of just choosing one out of two forms to prevent variation, we should choose one. Additionally, there are also interesting technical reasons to prefer interface. That page quotes the TypeScript team lead: Honestly, my take is that it should really just be interfaces for anything that they can model. There is no benefit to type aliases when there are so many issues around display/perf.
interface は type の型アサーションとの選択。
読み終わりました。