Closed23

TypeScript の型をお勉強する

ganyariyaganyariya

ジェネリクスの型は、宣言した時と同様 ジェネリクスの変数が入った状態になっている

const call: <T>(arg: T) => void = <T>(arg: T): void => {
  console.log(arg);
};
ganyariyaganyariya

返り値の型指定の箇所にも変数名を利用できる。
すると、実際の関数宣言のところで、その変数名を利用できる。

また、U extends any[] は、Uが any[] の部分型である必要があることを示す。
string[] や タプル型もすべて、any[] の部分型である。

以下の例では、U = number[] と推論される

const bind = <T, U extends any[], R>(
  func: (arg: T, ...rest: U) => R,
  value: T
): ((...args: U) => R) => {
  return (...args: U) => {
    console.log(args);
    return func(value, ...args);
  };
};

const add = (x: number, y: number) => x + y;
const add1 = bind(add, 1);

console.log(add1(5));

ganyariyaganyariya

Union は与えられた 型 A| B| C| D| E のうち、どれかであればよいことを表す

ganyariyaganyariya

null check をすると、nullを外したものを TSが推論する。
v?は null でないならばアクセスする。もし null なら undefined を帰す

const func = (v: string | null): number => {
  if (v != null) return v.length;
  else return 0;
};

const func2 = (v: string | null): number => {
  return v?.length ?? 0;
};
ganyariyaganyariya

never: 属する値が存在しない型
すべて型の部分型になっている

部分型がよくわかってないけど、以下のようなもの。
{age: number} は A という interface の部分型となっている。
継承みたいに考えると、Aが子クラスで、{age: number} が親クラスみたいなもの。

部分型に対して代入は自由にできる。(より部分型のほうが抽象度が高くなり、親クラスに近づいていく。)

interface A {
  age: number;
  name: string;
}

const a: A = { age: 10, name: "" };
const b: { age: number } = a;
ganyariyaganyariya

以下のように絶対にこないようなときに、never という型が自動で設定される。
また、例外などを故意に起こすなど、絶対に値を返さない、としたい場合は明示的に never を書く

なので、基本的に never は自分では書かない

interface Some<T> {
  type: "Some";
  value: T;
}
interface None {
  type: "None";
}
type Option<T> = Some<T> | None;

function map<T, U>(obj: Option<T>, f: (obj: T) => U): Option<U> {
  switch (obj.type) {
    case "Some":
      return {
        type: "Some",
        value: f(obj.value),
      };
    case "None":
      return {
        type: "None",
      };
    default:
      return obj; // ここには絶対にこないので never という型になっている
  }
}

function func(): never {
  throw new Error('Hi');
}
ganyariyaganyariya

交叉型は T & U である、つまり T でもあり、Uとも解釈できる型を指す。
(イメージ的には foo のみを持つ型って考えてたけど違った。
TもUも両方含む必要がある、というものだった)

interface Hoge {
  foo: string;
  bar: number;
}
interface Piyo {
  foo: string;
  baz: number;
}

type HogePiyo = Hoge & Piyo;

const obj: HogePiyo = {
  foo: "",
  bar: 20,
  baz: 30,
};

ganyariyaganyariya

x? とすると、undefined との union になる。
また、readonly variable: T をつけると、const のようになる

ganyariyaganyariya

インデックスシグネチャ。
keyという部分はなんでもいい tmpとかでもいい。
Mapを極力使ったほうがいいっぽい

interface MyObj {
  [key: string] : number;
}
ganyariyaganyariya

関数も interface に書ける
複数の型を書くと、オーバーロードのように表現できる

interface Func {
  (age: number, name: string): void;
}

const f: Func = (age: number, name: string) => {
  console.log(age, name);
};
ganyariyaganyariya

ParentクラスとChildクラスがあったときに、
ChildインスタンスをParentに入れるのがアップキャスト。
ParentインスタンスをChildに入れるのでダウンキャスト。

as はダウンキャストもアップキャストもできる。
基本ダウンキャストにだけつかおう(アップキャストにasはいらないので)

function rand(): string | number {
  if (Math.random() < 0.5) return "hello";
  else return 200;
}
const value = rand();

const str = value as number;
console.log(str);
ganyariyaganyariya

タプル型を推論する(...をつけると、展開してそれらの型だと推論する)

const removeFirst = <T, Rest extends readonly unknown[]>(
  arr: [T, ...Rest]
): Rest => {
  const [, ...rest] = arr;
  return rest;
};

const arr = removeFirst([1, 2, 3, "foo"]);
ganyariyaganyariya

x.a = 20 でエラーが出る。
as const すると、変数をその値に制限する。(以下ならx.a = "10" という 10 リテラル型になる)
readonly にあると考えるといい

const x = {
  a: 10,
  b: 30,
} as const;

x.a = 20;
ganyariyaganyariya

unknown は、すべての型を部分型(子)として持つ。
そのため、const x: unknown = 40 など、やんちゃができる

any と違うのは、ほとんどすべての操作を制限できる ところである。

const s = x + 5は、x が unknown 、つまりどのような演算ができるかわからない! と推論されてエラーができる。

any が強制的になんでもあり! であうならば
unknown はほぼすべてだめ! とみなせる

ganyariyaganyariya

typeof は変数の型を取り出す。

const x = 1;
type T = typeof x;
ganyariyaganyariya

keyof T は、Tの key要素のUnionを返す。
そのため、x には、 "foo", "bar" が入る

interface A {
  foo: string;
  bar: number;
}

let x: keyof A;
x = "foo";
ganyariyaganyariya

Lookup Types 型 T[K]
型T の 要素Kを 用いて、Kの型 T[K]をプロパティのように取り出す

以下の場合 T = A, K = "foo".
"foo" 型 というのがポイント
そして、A["foo"] は string となる

interface A {
  foo: string;
  bar: number;
}
const x: A["foo"] = "hello";

ganyariyaganyariya

以下は、lookup types の応用である。

まず、 K extends keyof T は Tのプロパティ名のUnionである。
以下の例であれば K = foo | bar となる。

そして、T[K] は、オブジェクトT で取り出されたプロパティ名の型自体を指す。
以下の例であれば Obj["foo"] は Obj interface の foo 属性と考えられるので、
Obj=T[K="foo"] = stringとなる。

よって、以下の pick は、オブジェクトから「プロパティ名」を指定して そのプロパティの型を保証して値を返す関数である。

function pick<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

interface Obj {
  foo: string;
  bar: number;
}

const obj: Obj = {
  foo: "nyann",
  bar: 123,
};

const str = pick(obj, "foo");
このスクラップは2022/01/07にクローズされました