Open17

TypeScriptの型関連についてメモ

kawakenkawaken

Utility Types

Partial<Type>
Readonly<Type>
Record<Keys,Type>
Pick<Type, Keys>
Omit<Type, Keys>
Exclude<Type, ExcludedUnion>
Extract<Type, Union>
NonNullable<Type>
Parameters<Type>
ConstructorParameters<Type>
ReturnType<Type>
InstanceType<Type>
Required<Type>
ThisParameterType<Type>
OmitThisParameter<Type>
ThisType<Type>
kawakenkawaken

Genericsの型パラメータからインスタンス化する

Generics are erased during compilation.

型パラメータの情報はコンパイルされるときに消えちゃう(Javaと同じぽい)ので、型パラメータを利用したインスタンス化できない。以下のような回避策がある。

function create<T>(ctor: { new(): T }) {
  return new ctor();
}
var c = create(MyClass); // c: MyClass

function isReallyInstanceOf<T>(ctor: { new(...args: any[]): T }, obj: T) {
  return obj instanceof ctor;
}
kawakenkawaken

never

never - TypeScript Deep Dive 日本語版

  • any がtop型
  • never がbottom型

常に throw するとか、絶対 return しないとか、そういうときに使う。Kotlinと同じぽい。

function foo(x: string | number): boolean {
  if (typeof x === "string") {
    return true;
  } else if (typeof x === "number") {
    return false;
  }

  // Without a never type we would error :
  // - Not all code paths return a value (strict null checks)
  // - Or Unreachable code detected
  // But because TypeScript understands that `fail` function returns `never`
  // It can allow you to call it as you might be using it for runtime safety / exhaustive checks.
  return fail("Unexhaustive!");
}

function fail(message: string): never { throw new Error(message); }
kawakenkawaken

keyof

プロパティ名のユニオン型が取得できる。

interface Person {
  name: string;
  age: number;
  location: string;
}

type K1 = keyof Person; // "name" | "age" | "location"
type K2 = keyof Person[]; // "length" | "push" | "pop" | "concat" | ...
type K3 = keyof { [x: string]: Person }; // string

最後の K3 はPlaygroundでやってみると string | number になっていた。

kawakenkawaken

Lookup types

keyof と一緒に導入された。

型に対して配列のようにプロパティ名を渡すと相当する型が返ってくる。

interface Person {
  name: string;
  age: number;
  location: string;
}

type P1 = Person["name"]; // string
type P2 = Person["name" | "age"]; // string | number
type P3 = string["charAt"]; // (pos: number) => string
type P4 = string[]["push"]; // (...items: string[]) => number
type P5 = string[][0]; // string

プロパティ名というか、メンバー関数名を指定すると、関数の型が返ってくる。

kawakenkawaken

ValueOf<T>

type V = Person[keyof Person]; // string | number

keyofLookup Types の組み合わせで値のみの型が取得できる。
これを利用して Valueof<T> という型を作ることができる。

type ValueOf<T> = T[keyof T];
type V2 = ValueOf<Person>;

keyof が小文字なので valueof としたいけど型だから Valueof にした方が良いのかどうか。

kawakenkawaken

Erased Structural Types

TypeScriptでは実行時に型の情報はなくなってしまう。

Because TypeScript’s type system is fully erased, information about e.g. the instantiation of a generic type parameter is not available at runtime.

class もそうなのかな?

class Car {};
var c = new Car();
console.log(typeof c);            // "object"
console.log(c instanceof Car);    // true

class Fish {};
console.log(c instanceof Fish);   // false
console.log(c instanceof Object); // true

instanceof は識別できるらしい。

kawakenkawaken

Index signature(インデックス型)

JavaScript では以下のようなObjectを辞書のようにして利用できる。

var v = {a:1};
console.log(v.a);    // 1
console.log(v['a']); // 1

v['a']'a' の部分をインデックスシグネチャというらしい。

JavaScriptでは object を利用できるが、TypeScriptでは stringnumber しか利用できない。

kawakenkawaken

インデックスシグネチャは定義しないと使えない

interface Foo {
  x: number;
  y: number;
  //[index: string]: number;
}

var foo: Foo = {x: 10, y: 20};
console.log(foo['x']); // ここはエラーにならない

var x = 'x';
console.log(foo[x]); // ここでエラー

↑このようにインデックスシグネチャとして利用できるプロパティの宣言をしていないと、変数を利用したインデックスシグネチャの利用でエラーになる。

Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'Foo'. No index signature with a parameter of type 'string' was found on type 'Foo'.

interface Foo {
  x: number;
  y: number;
  [index: string]: number;
}

var foo: Foo = {x: 10, y: 20};

var x = 'x';
console.log(foo[x]); // 10

これだとエラーにならない。

kawakenkawaken

Generic parameter defaults

ジェネリクスを使用する際に型パラメータのデフォルト値を設定することができる。

そこで、デフォルト型パラメーターとしてErrorを指定することでジェネリクスの型Tは必要な時だけ指定して、何も指定してない場合は自動でErrorとする事ができます。

type MyErrorEvent<T = Error> = {
    error: T;
    type: string;
}

// デフォルト型パラメータを指定した事で Error の型指定を省略できる
const errorEvent: MyErrorEvent = { error: new Error('エラーです'), type: 'syntax' }
const networkErrorEvent: MyErrorEvent<NetworkAccessError> = { error: new NetworkAccessError('ネットワークエラーです'), type: 'nextwork' }

extends とも併用できるので以下のようにも記述できる。

MyErrorEventをに与えられる型TをErrorのサブクラスに限定しつつ、省略時はSyntaxErrorとしたい場合は次のような書き方になります。

type MyErrorEvent<T extends Error = SyntaxError> = {
  error: T;
  type: string;
}