⏰
TypeScript 3.7 で止まった知識をアップデートしたメモ(4.2まで)
こんにちは、 nerikosans です。
TypeScript 4.3 RCが 発表された そうですね。
これはそろそろ知識をアップデートしないとなと思い、長らく3.7~8あたりで止まっていた知識をアップデートすべく、公式のWhat's newを読みながら、気になった機能についてメモを書いていこうと思います。
TypeScript 3.8
Type-only import/export
-
import type
により、typeのみをimportできるようになった。 - TSの文脈でのみ有効な指定であり、トランスパイル時にはこの記述は完全に削除される。
import type { HogeType } from './myModule';
Private fields
- classのfieldを
#
で始めることで、完全にprivateなfieldを宣言できるようになった。
class Person {
#name: string;
constructor(name: string) {
this.#name = name;
}
}
- classに完全に固有のfieldとなり、継承先で同じ名前のfieldを宣言しても別のものと認識される。
-
private
修飾子による宣言と違って、js runtimeからもアクセスはできない。
そのほか
-
*
をas
付きで直接exportできるようになった。
export * as myName from 'someModule';
TypeScript 3.9
3.9ではSyntaxに大きな変更はなく、挙動の修正などが多かったようです。
Type Intersectionの挙動変更
-
C
型の変数にA & B
型の変数を代入するときのチェックが改善された。 - 従来は
A
かB
のどちらかがC
に代入可能なら、A & B
型も代入可能だった。- 下の例では、
B
がC
に代入可能なので、エラーなし
- 下の例では、
- 修正後は、
A & B
型全体がC
に代入可能であることが求められる。- 下の例では、プロパティ
a
があっていないので、エラー
- 下の例では、プロパティ
interface A {
a: number; // notice this is 'number'
}
interface B {
b: string;
}
interface C {
a?: boolean; // notice this is 'boolean'
b: string;
}
declare let x: A & B;
declare let y: C;
y = x;
公式サンプルより
Type 'A & B' is not assignable to type 'C'.
Types of property 'a' are incompatible.
Type 'number' is not assignable to type 'boolean | undefined'.
このエラーが出るようになった
Typescript 4.0
Variadic Tuple Types
- Tuple Type内でgeneric typeをspread (
...T
) できるようになった。
// 配列から最初の要素を抜いて返す関数
function tail<T extends any[]>(arr: readonly [any, ...T]) {
const [_ignored, ...rest] = arr;
return rest;
}
- Tuple Type内で、末尾以外でもspreadが使用できるようになった。
type Strings = [string, string];
type Numbers = number[];
type Unbounded = [...Strings, ...Numbers, boolean];
- 合わせ技1: 2つの配列を結合する関数
type Arr = readonly any[];
function concat<T extends Arr, U extends Arr>(arr1: T, arr2: U): [...T, ...U] {
return [...arr1, ...arr2];
}
- 合わせ技2: 関数の一部の引数だけを先に渡す関数
type Arr = readonly unknown[];
function partialCall<T extends Arr, U extends Arr, R>(
f: (...args: [...T, ...U]) => R,
...headArgs: T
) {
return (...tailArgs: U) => f(...headArgs, ...tailArgs);
}
Labeled Tuple Elements
- Tuple Typeにlabelをつけられるようになった。
type Range = [number, number]; // これを
type LabeledRange = [start: number, end: number]; // こう
- Overloadを書くときとかに便利
type Name =
| [first: string, last: string]
| [first: string, middle: string, last: string];
function createPerson(...name: Name) {}
const p1 = createPerson("Taro", "Yamada");
const p2 = createPerson("Taro", "Harris", "Yamada");
New assignment operators
-
&&=
,||=
,??=
が使えるようになった。 - このoperatorを使うと、必要なときだけ右辺の評価と代入がされるようになって、速くなるかも。
const obj = {
get prop() {
console.log("getter has run");
return Math.random() < 0.5;
},
set prop(_val: boolean) {
console.log("setter has run");
}
};
function foo() {
console.log("right side evaluated");
return true;
}
// 常にfoo()を評価してsetterが走る
obj.prop = obj.prop || foo();
// 必要なときだけfoo()を評価してsetterが走る
obj.prop ||= foo();
そのほか
-
try {} catch (e) {}
したときにe: unknown
を指定できるようになった。 -
jsxFragmentFactory
オプションでFragmentのコンパイル方法を指定できるようになった。
Typescript 4.1
TypeScript: Documentation - TypeScript 4.1
Template Literal Types
- 文字列typeを宣言するとき、Template Literalを使えるようになった。
type World = "world";
type Greeting = `hello ${World}`;
// type Greeting = "hello world"
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;
-
Uppercase<T>
,Lowercase<T>
,Capitalize<T>
,Uncapitalize<T>
が使えるようになった。
type HELLO = `${Uppercase<"hello">}` // => "HELLO"
Key Remapping in Mapped Types
- Mapped Typeを使うとき、
as
によってkey typeを変換して扱えるようになった。
type MappedTypeWithNewKeys<T> = {
[K in keyof T as NewKeyType]: T[K]
}
- 例
type Getters<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K]
};
interface Person {
name: string;
age: number;
location: string;
}
type LazyPerson = Getters<Person>;
/*
type LazyPerson = {
getName: () => string;
getAge: () => number;
getLocation: () => string;
}
*/
Typescript 4.2
Leading/Middle Rest Elements in Tuple Types
- Tuple Type内で、末尾以外でもrest element(
...string[]
とか) を使えるようになった。- ただし、1つのTuple内で1箇所だけ。
- かつ、optional elementとは同居できない。
type A = [...string[], number]; // OK
type B = [boolean, ...string[], boolean]; // OK
type C = [...string[], ...number[]];
// Error: A rest element cannot follow another rest element.
type D = [...string[], number?];
// Error: An optional element cannot follow a rest element.
Variadic Tuple Types(TS4.0)との関係は?
- そもそも4.0の時点で以下のように書けた。
- 外部で定義した型をspreadするぶんにはどこでもよい、ということらしい
- というより、restが末尾になるように無理やり解釈されていそう
TS 4.0.5
type Strings = string[];
type Numbers = number[];
type Pair = [number, number];
type A = [boolean, ...Strings, boolean]; // OK
type B = [boolean, ...string[], boolean]; // NG
// Error: A rest element must be last in a tuple type.
type C = [boolean, ...Strings, ...Numbers, boolean]; // OK
// type C = [boolean, ...(string | number | boolean)[]]
type D = [boolean, ...Strings, ...Pair, boolean]; // OK
// type D = [boolean, ...(string | number | boolean)[]]
- 4.2だと以下のようになる。
- 外部の型を複数spreadしたときの挙動が賢くなっている (type
C
,D
)
- 外部の型を複数spreadしたときの挙動が賢くなっている (type
TS 4.2.3
type A = [boolean, ...Strings, boolean]; // OK
type B = [boolean, ...string[], boolean]; // OK
type C = [boolean, ...Strings, ...Numbers, boolean]; // OK
// type C = [boolean, ...(string | number)[], boolean]
type D = [boolean, ...Strings, ...Pair, boolean]; // OK
// type D = [boolean, ...string[], number, number, boolean]
type E = [boolean, ...string[], ...number[], boolean]; // NG
// Error: A rest element cannot follow another rest element.
Destructured Variables Can Be Explicitly Marked as Unused
- destructionによって変数定義をするとき、
_
を最初につけると、「使わない」変数として扱えるようになった。-
noUnusedLocals
が出なくなる
-
let [_first, second] = getValues();
// _first を使わなくても良い
おわり
こんなもんでしょうか。
すべての変更は網羅していませんが、いろいろナウいことを勉強できてよかったです。
ここに書かなかった変更について、Index Signatureの周りでいろいろ議論がありそうだったので、また調べて書くかもしれません。
ではまた!
Discussion