本文へスキップ

TypeScript バージョン別の便利なパターン

TypeScript のバージョンアップに伴い、新しい方法が導入されることがよくあります。このセクションでは、React + TypeScript の現在のユーザーが TypeScript のバージョンアップを行い、TypeScript + React アプリケーションやライブラリで一般的に使用されているパターンを探求するのに役立つ情報を提供します。他のセクションと重複している部分があるかもしれませんが、矛盾点を見つけた場合は、issue を報告してください

2.9 より前の TypeScript バージョンガイドは未作成です。PR を送っていただければ幸いです! 公式 TS チームのコミュニケーション以外にも、Marius Schulz のブログのバージョンノート もお勧めします。TypeScript の歴史の詳細については、A Brief History of TypeScript TypesA Brief History of DefinitelyTyped を参照してください。prop-typesomreason-reacttyped-react のように、あまり知られていない React の代替型付けも検討したい場合があります。

TypeScript 2.9

[リリースノート | ブログ投稿]

  1. タグ付きテンプレート文字列の型引数(例:`styled-components`)
export interface InputFormProps {
foo: string; // this is understood inside the template string below
}

export const InputForm = styledInput<InputFormProps>`
color:
${({ themeName }) => (themeName === "dark" ? "black" : "white")};
border-color: ${({ foo }) => (foo ? "red" : "black")};
`;
  1. JSX ジェネリクス

https://github.com/Microsoft/TypeScript/pull/22415

ジェネリックコンポーネントの型付け/使用に役立ちます

// instead of
<Formik
render={(props: FormikProps<Values>) => {
/* your code here ... */
}}
/>;

// usage
<Formik<Values>
render={(props) => {
/* your code here ... */
}}
/>;
<MyComponent<number> data={12} />;

詳細情報:https://github.com/basarat/typescript-book/blob/master/docs/jsx/react.md#react-jsx-tip-generic-components

TypeScript 3.0

[リリースノート | ブログ投稿]

  1. 可変長引数の記述のための型付き rest パラメータ
// `rest` accepts any number of strings - even none!
function foo(...rest: string[]) {
// ...
}

foo("hello"); // works
foo("hello", "world"); // also works
  1. `LibraryManagedAttributes` を使用した JSX での `propTypes` と `static defaultProps` のサポート
export interface Props {
name: string;
}

export class Greet extends React.Component<Props> {
render() {
const { name } = this.props;
return <div>Hello ${name.toUpperCase()}!</div>;
}
static defaultProps = { name: "world" };
}

// Type-checks! No type assertions needed!
let el = <Greet />;
  1. 新しい `unknown` 型

APIへの型付けを行い、型チェックを強制するため - React に固有のものではないが、APIレスポンスの処理に非常に便利

interface IComment {
date: Date;
message: string;
}

interface IDataService1 {
getData(): any;
}

let service1: IDataService1;
const response = service1.getData();
response.a.b.c.d; // RUNTIME ERROR

// ----- compare with -------

interface IDataService2 {
getData(): unknown; // ooo
}

let service2: IDataService2;
const response2 = service2.getData();
// response2.a.b.c.d; // COMPILE TIME ERROR if you do this

if (typeof response === "string") {
console.log(response.toUpperCase()); // `response` now has type 'string'
}

TODO: この変更の原因を特定する。これが何をすべきだったのかわからない

`unknown` 型に対して型アサーションを行うか、**型ガード**を使用できます。これは `any` を使用するよりも優れています。

  1. プロジェクト参照

プロジェクト参照により、TypeScript プロジェクトは他の TypeScript プロジェクトに依存できるようになります。具体的には、`tsconfig.json` ファイルが他の `tsconfig.json` ファイルを参照できるようにします。これにより、コードベース全体を毎回再コンパイルすることなく、複数のプロジェクトに分割することで、大規模なコードベースをスケールできます。

各フォルダに、少なくとも以下のものを含む `tsconfig.json` を作成します

{
"compilerOptions": {
"composite": true, // tells TSC it is a subproject of a larger project
"declaration": true, // emit .d.ts declaration files since project references dont have access to source ts files. important for project references to work!
"declarationMap": true, // sourcemaps for .d.ts
"rootDir": "." // specify compile it relative to root project at .
},
"include": ["./**/*.ts"],
"references": [
// (optional) array of subprojects your subproject depends on
{
"path": "../myreferencedproject", // must have tsconfig.json
"prepend": true // concatenate js and sourcemaps generated by this subproject, if and only if using outFile
}
]
}

そして、最上位のサブプロジェクトを参照するルート `tsconfig.json`

{
"files": [],
"references": [{ "path": "./proj1" }, { "path": "./proj2" }]
}

そして、`tsc --build` または `tsc -b` を実行する必要があります。

`tsconfig` のボイラープレートを保存するには、`extends` オプションを使用できます

{
"extends": "../tsconfig.base"
// more stuff here
}

TypeScript 3.1

[リリースノート | ブログ投稿]

  1. 関数上のプロパティ宣言

このように関数にプロパティを付けることが「そのまま」機能するようになりました。

export const FooComponent = ({ name }) => <div>Hello! I am {name}</div>;

FooComponent.defaultProps = {
name: "swyx",
};

TypeScript 3.2

[リリースノート | ブログ投稿]

React に固有のものは特にありません。

TypeScript 3.3

[リリースノート | ブログ投稿]

React に固有のものは特にありません。

TypeScript 3.4

[リリースノート | ブログ投稿]

  1. `const` アサーション
function useLoading() {
const [isLoading, setState] = useState(false);

const load = (aPromise: Promise<any>) => {
setState(true);
return aPromise.finally(() => setState(false));
};

return [isLoading, load] as const; // infers [boolean, typeof load] instead of (boolean | typeof load)[]
}

const アサーション を使用できる場所に関する詳細情報。

TypeScript 3.5

[リリースノート | ブログ投稿]

  1. ビルトイン `` 型!

  2. ジェネリックコンストラクタからの高階型推論

type ComponentClass<P> = new (props: P) => Component<P>;
declare class Component<P> {
props: P;
constructor(props: P);
}

declare function myHoc<P>(C: ComponentClass<P>): ComponentClass<P>;

type NestedProps<T> = { foo: number; stuff: T };

declare class GenericComponent<T> extends Component<NestedProps<T>> {}

// type is 'new <T>(props: NestedProps<T>) => Component<NestedProps<T>>'
const GenericComponent2 = myHoc(GenericComponent);

Google が 3.5 にアップグレードした際のノート も参照してください。

TypeScript 3.6

[リリースノート | ブログ投稿]

React に特に関連するものは何もありませんが、プレイグラウンド がアップグレードされ、Ambient Classes and Functions Can Merge が追加されました。

TypeScript 3.7

[リリースノート | ブログ投稿]

  1. オプションチェーン
let x = foo?.bar.baz();

// is equivalent to

let x = foo === null || foo === undefined ? undefined : foo.bar.baz();

// Optional Element access
function tryGetFirstElement<T>(arr?: T[]) {
return arr?.[0];
}

// Optional Call
async function makeRequest(url: string, log?: (msg: string) => void) {
log?.(`Request started at ${new Date().toISOString()}`);
const result = (await fetch(url)).json();
log?.(`Request finished at at ${new Date().toISOString()}`);
return result;
}
  1. ナル結合演算子
let x = foo ?? bar();

// equivalent to

let x = foo !== null && foo !== undefined ? foo : bar();

通常 `||` を使用する場所では `??` を使用する必要があります。偽りであることを本当に意味する場合を除きます。

function ShowNumber({ value }: { value: number }) {
let _value = value || 0.5; // will replace 0 with 0.5 even if user really means 0
// etc...
}
  1. アサーション関数
function assert(condition: any, msg?: string): asserts condition {
if (!condition) {
throw new AssertionError(msg);
}
}
function yell(str) {
assert(typeof str === "string");

return str.toUppercase();
// ~~~~~~~~~~~
// error: Property 'toUppercase' does not exist on type 'string'.
// Did you mean 'toUpperCase'?
}

カスタム関数を使用せずにアサーションを行うこともできます。

function assertIsString(val: any): asserts val is string {
if (typeof val !== "string") {
throw new AssertionError("Not a string!");
}
}
function yell(str: any) {
assertIsString(str);

// Now TypeScript knows that 'str' is a 'string'.

return str.toUppercase();
// ~~~~~~~~~~~
// error: Property 'toUppercase' does not exist on type 'string'.
// Did you mean 'toUpperCase'?
}
  1. ts-nocheck

TypeScript ファイルの先頭に `// @ts-nocheck` を追加できるようになりました!マイグレーションに役立ちます。

TypeScript 3.8

[リリースノート | ブログ投稿]

  1. 型専用のインポートとエクスポート
import type { SomeThing } from "./some-module.js";

export type { SomeThing };
  1. ECMAScript プライベートフィールド

React に固有のものではないが、Bloomberg でOK

  1. `export * as ns` 構文

これは ES2020 構文です。以下の代わりに

import * as utilities from "./utilities.js";
export { utilities };

以下のようにできます

export * as utilities from "./utilities.js";
  1. トップレベル `await`

React に固有のものではないが、Myles に感謝

  1. JSDoc プロパティ修飾子

JSDoc ユーザーにとって便利 - `@public, @private, @protected, @readonly`

  1. Linux でのより良いディレクトリ監視と watchOptions
  2. 「高速で緩い」増分チェック

`assumeChangesOnlyAffectDirectDependencies` は、非常に大規模なコードベースのビルド時間を短縮します。

TypeScript 3.9

[リリースノート | ブログ投稿]

  1. (マイナー機能) 新しい `ts-expect-error` ディレクティブ。

エラーが発生すると予想されるテストを作成する場合に使用します。

// @ts-expect-error
console.log(47 * "octopus");

`ts-expect-error` を選択する状況

  • 型システムが操作でエラーすることを実際に期待するテストコードを書いている場合
  • かなりすぐに修正が来ることを期待しており、簡単な回避策が必要な場合
  • 比較的規模の大きなプロジェクトで、影響を受けるコードが再度有効になったら抑制コメントを削除したい積極的なチームがいる場合

`ts-ignore` を選択する状況

  • 大規模なプロジェクトで、明確な所有者がいないコードに新しいエラーが表示された場合
  • 2つの異なるバージョンのTypeScript間のアップグレードの途中で、あるバージョンではコード行がエラーするが、別のバージョンではエラーしない場合。
  • 正直言って、どちらのオプションが優れているかを判断する時間がない場合。
  1. `}` と `>` は、無効な JSX テキスト文字になりました

常に無効でしたが、TypeScript と Babel がそれを強制するようになりました。

Unexpected token. Did you mean `{'>'}` or `&gt;`?
Unexpected token. Did you mean `{'}'}` or `&rbrace;`?

必要に応じて、一括で変換できます

TypeScript 4.0

[リリースノート | ブログ投稿]

Preact を使用したカスタムプラグマ用です。

// Note: these pragma comments need to be written
// with a JSDoc-style multiline syntax to take effect.
/** @jsx h */
/** @jsxFrag Fragment */

import { h, Fragment } from "preact";

let stuff = (
<>
<div>Hello</div>
</>
);
// transformed to
let stuff = h(Fragment, null, h("div", null, "Hello"));

TypeScript 4.1

[リリースノート | ブログ投稿]

  1. テンプレートリテラル型

これは非常に大きな機能です。

ユースケース 1 - 他の文字列リテラル型の順列から文字列リテラル型を生成する

type VerticalAlignment = "top" | "middle" | "bottom";
type HorizontalAlignment = "left" | "center" | "right";

// Takes
// | "top-left" | "top-center" | "top-right"
// | "middle-left" | "middle-center" | "middle-right"
// | "bottom-left" | "bottom-center" | "bottom-right"
declare function setAlignment(
value: `${VerticalAlignment}-${HorizontalAlignment}`
): void;

setAlignment("top-left"); // works!
setAlignment("top-middel"); // error!
setAlignment("top-pot"); // error! but good doughnuts if you're ever in Seattle

ユースケース 2 - 動的な文字列リテラル型のモデリング

type PropEventSource<T> = {
on(eventName: `${string & keyof T}Changed`, callback: () => void): void;
};

/// Create a "watched object" with an 'on' method
/// so that you can watch for changes to properties.
declare function makeWatchedObject<T>(obj: T): T & PropEventSource<T>;

文字列操作を容易にするために、新しいジェネリック `Uppercase`、`Lowercase`、`Capitalize`、`Uncapitalize` が追加されました。

これらは `infer` キーワードと組み合わせることができます。詳細はこちら

type ParseRouteParams<Rte> = Rte extends `${string}/:${infer P}` ? P : never;

type Params = ParseRouteParams<"/api/user/:userID">; // Params is "userID"

type NoParams = ParseRouteParams<"/api/user">; // NoParams is never --> no params!

この機能は非常に柔軟性があります。他のユースケースのアイデアはこちらをご覧ください。

  1. React 17 JSX ファクトリ

これは、React 17 のサポート全般に合わせて出力を行うための新しいコンパイラオプションです。

// ./src/tsconfig.json - for production
{
"compilerOptions": {
"module": "esnext",
"target": "es2015",
"jsx": "react-jsx",
"strict": true
},
"include": [
"./**/*"
]
}

// ./src/tsconfig.dev.json - for development - extending the production config
{
"extends": "./tsconfig.json",
"compilerOptions": {
"jsx": "react-jsxdev"
}
}

その他

  1. マップされた型におけるキーのリマッピング
  2. 再帰的な条件付き型
  3. チェックされたインデックス付きアクセス

TypeScript 4.2

[リリースノート | ブログ記事]

React固有の変更はありません

TypeScript 4.3

[リリースノート | ブログ記事]

React固有の変更はありません

TypeScript 4.4

[リリースノート | ブログ記事]

React固有の変更はありません

TypeScript 4.5

[リリースノート | ブログ記事]

  1. (軽微なVSCodeの改善) JSX属性のスニペット補完

TypeScript 4.6

[リリースノート | ブログ記事]

  1. (非常に軽微) `react-jsx`コンパイル出力における不要な引数の削除

TypeScript ロードマップと仕様

https://github.com/Microsoft/TypeScript/wiki/Roadmap

TypeScriptの仕様書もオンラインで読めることをご存知でしたか? https://github.com/microsoft/TypeScript/blob/master/doc/spec-ARCHIVED.md