TypeScriptのTips集
この記事は TypeScript Advent Calendar 2020 の 17 日目です。
今回は TypeScript のTipsをいくつかあげていきたいと思います!
割と基本的なものが多いので、普段から TypeScript をがっつり書いている方に取ってはすでに知っているものが多いかもしれません。ただ、意識しないとあまり使わなかったり、TypeScript の経験がない方に取っては新鮮なものもあるのでは?と思ったので書いてみました。
keyof
オブジェクトのキーを union 型に
type Post = {
id: number;
title: string;
content: string;
};
type PostKey = keyof Post;
// type PostKey = "id" | "title" | "content"
使うときはこんな感じでしょうか
const sortBy = <T extends object, K extends keyof T>(
objects: T[],
key: K
): T[] => {
return objects.sort((a, b) => a[`${key}`] - b[`${key}`]);
};
const posts = [
{ id: 1, title: 'test1' },
{ id: 3, title: 'test3' },
{ id: 2, title: 'test2' },
];
console.log(sortBy(posts, 'id'));
// [
// { id: 1, title: 'test1' },
// { id: 2, title: 'test2' },
// { id: 3, title: 'test3' }
// ]
console.log(sortBy(posts, 'name'));
// Argument of type '"name"' is not assignable to parameter of type '"id" | "title"'
lookup
例えば API の返り値が次の型のようなとき
(GitHub API を参考にしています)
以下のように type のプロパティの型を取り出すことができる
type Response = {
repository: {
id: string;
homepageUrl: string;
issues: {
edges: {
node: {
author: {
url: string;
};
body: string;
};
};
totalCount: number;
}[];
};
};
type Issues = Response['repository']['issues'];
// type Issues = {
// edges: {
// node: {
// author: {
// url: string;
// };
// body: string;
// };
// };
// totalCount: number;
// }[]
type IssueTotalCount = Issues[0]['totalCount'];
// type IssueTotalCount = number
utility
Partial
フィールド全てが Optional になる
type Post = {
id: number;
title: string;
};
type PartialPost = Partial<Post>;
// type PartialPost = {
// id?: number;
// title?: string;
// }
const post1: PartialPost = { id: 1 };
Required
フィールド全てが必須になる
type Post = {
id: number;
title: string;
userId?: number;
};
type RequiredPost = Required<Post>;
// type RequiredPost = {
// id: number;
// title: string;
// userId: number;
// }
Readonly
フィールド全てが 読み込み専用 になる
type Post = {
id: number;
title: string;
};
type ReadonlyPost = Readonly<Post>;
// type ReadonlyPost = {
// readonly id: number;
// readonly title: string;
// };
const readonlyPost: ReadonlyPost = { id: 1, title: 'readonly' };
readonlyPost.title = 'update';
// Cannot assign to 'title' because it is a read-only property.
Pick
指定した key だけ取り出す
type Post = {
id: number;
title: string;
};
type PickPost = Pick<Post, 'id'>;
// type PickPost = {
// id: number;
// }
Omit
指定した key を除外する
type Post = {
id: number;
title: string;
content: string;
};
type OmitPost = Omit<Post, 'id' | 'title'>;
// type OmitPost = {
// content: string;
// }
Exclude
union 型から除外する
type RGB = 'red' | 'green' | 'blue';
type RG = Exclude<RGB, 'blue'>;
// type RG = "red" | "green"
ReturnType
関数の戻り値の型を取得できる
const add = (a: number, b: number): number => {
return a + b;
};
type AddReturnType = ReturnType<typeof add>;
// type AddReturnType = number
他にも色々あります。
utility-types
as const
as const
を宣言すると、それ以降は型推論で型が拡大せず、readonly になる。
let constPost = { id: 1, title: 'title', content: 'content' } as const;
// let constPost: {
// readonly id: 1;
// readonly title: "title";
// readonly content: "content";
// }
enum は使うべきでないという話
よく言われているが、enum は非推奨
さようなら、TypeScript enum
TypeScript の enum を使わないほうがいい理由を、Tree-shaking の観点で紹介します
enum の代替としては次の感じ
const Country = {
JAPAN: 'JAPAN',
USA: 'USA',
CHINA: 'CHINA',
} as const;
type Country = typeof Country[keyof typeof Country];
アサーション
null でないことが確信できる場合は、明示的に null でないことを指定できる
document.getElementById("test").innerHTML
// Object is possibly 'null'.
document.getElementById("test")!.innerHTML
// コンパイルエラーなし
let text;
if (...) {
text = 'text1'
} else if (...) {
text = 'text2'
}
text.substr(...)
// let text: string | undefined
// Object is possibly 'undefined'
text!.substr(...)
//コンパイルエラーなし
基本的には非推奨なので、?
を使っておくのが無難
document.getElementById('test')?.innerHTML;
Template Literal Types
4.1 の目玉機能 Template Literal Types
template literal が型定義で使用できる。
一応 template literal を説明すると文字列の中に変数を埋め込める文法
const world = 'World';
console.log(`Hello ${world}!`);
// 'Hello World!';
type DataFormat = 'HTML' | 'JSON' | 'XML';
type Parser = `parse${DataFormat}`;
// type Parser = "parseHTML" | "parseJSON" | "parseXML"
type parser1: Parser = 'parseHTML'
type parser2: Parser = 'parseHogehoge'
// Type '"parseHogehoge"' is not assignable to type '"parseHTML" | "parseJSON" | "parseXML"'.
type Satoshi = `Satoshi${string}`;
const satoshi1: Satoshi = 'Satoshi';
const satoshi2: Satoshi = 'Satoshi Tanaka';
const notSatoshi: Satoshi = 'Taro Tanaka';
// Type '"Taro Tanaka"' is not assignable to type '`Satoshi${string}`'.
Template Literal Types があれば、もはや SQL にも型をつけることができる
ts-sql
最後に
TypeScript は柔軟に型が定義できて楽しいですが、普段から意識して使っていかないと勘が鈍るので積極的に使っていきたいですね。
Discussion
よく言われてると書いてるが、参考リンクは2つしかないですよね。
本当に非推奨ならtypescript公式等でそう名言されてるはずです。
treeshakingの問題に触れてますが、そもそも使わないenumを定義している時点でおかしいのではと疑問がある。他の言語でもenumは常識的に使用されているし、少数の意見で断定する言い方はよくないのでは?