Nominal Typing

Nominal Typing

TypeScriptの型システムは構造的です。そして、これはTypeScriptを使う理由の一つです。しかし、同じ構造を持っていても、2つの変数が異なる型名を持つ2つの変数を区別する必要があるシステムのユースケースがあります。非常に一般的な使用例は、identity構造(一般的にC#/Javaなどの言語において名前と関連するセマンティクスを持つただの文字列)です。
コミュニティでは、いくつかのパターンが登場しています。私の個人的な好みで降順に説明します:

リテラル型の使用

このパターンは、ジェネリックとリテラル型を使用します。
1
/** Generic Id type */
2
type Id<T extends string> = {
3
type: T,
4
value: string,
5
}
6
7
/** Specific Id types */
8
type FooId = Id<'foo'>;
9
type BarId = Id<'bar'>;
10
11
/** Optional: constructors functions */
12
const createFoo = (value: string): FooId => ({ type: 'foo', value });
13
const createBar = (value: string): BarId => ({ type: 'bar', value });
14
15
let foo = createFoo('sample')
16
let bar = createBar('sample');
17
18
foo = bar; // Error
19
foo = foo; // Okay
Copied!
    アドバンテージ
      どの型アサーションも必要ありません
    デメリット
      {type,value}のような構造は望ましくない可能性があり、サーバー側のシリアライズのサポートが必要です

Enumsを使う

TypeScriptのEnumsは、一定のレベルのnominal型付けを提供します。2つの列挙型は、名前が異なる場合、等しくありません。この事実を利用して、構造的に互換性のある型に対してnominal型付けを提供することができます。
この回避策には、以下が含まれます。
    種類を表すenumを作成する
    enumと実際の構造の(intersection &)としての型を作成する
下記のデモで示します。その構造の型はただの文字列です:
1
// FOO
2
enum FooIdBrand {}
3
type FooId = FooIdBrand & string;
4
5
// BAR
6
enum BarIdBrand {}
7
type BarId = BarIdBrand & string;
8
9
/**
10
* Usage Demo
11
*/
12
var fooId: FooId;
13
var barId: BarId;
14
15
// Safety!
16
fooId = barId; // error
17
barId = fooId; // error
18
19
// Newing up
20
fooId = 'foo' as FooId;
21
barId = 'bar' as BarId;
22
23
// Both types are compatible with the base
24
var str: string;
25
str = fooId;
26
str = barId;
Copied!

インターフェースの使用

numbersenumと型互換性があるため、これまでの手法は使用できません。代わりに、インターフェースを使用して構造の互換性を破ることができます。この方法はTypeScriptコンパイラチームによっても使用されているので、言及する価値があります。_プレフィックスとBrandサフィックスを使用する規約を強くお薦めします(そして、TypeScriptチームに採用されている規約です)。
この回避策には、以下が含まれます。
    構造上の互換性を破るために、型に未使用のプロパティを追加する。
    新しくオブジェクトを作成したり、ダウンキャストが必要な場合は、型アサーションを使用します。
これは以下のとおりです:
1
// FOO
2
interface FooId extends String {
3
_fooIdBrand: string; // To prevent type errors
4
}
5
6
// BAR
7
interface BarId extends String {
8
_barIdBrand: string; // To prevent type errors
9
}
10
11
/**
12
* Usage Demo
13
*/
14
var fooId: FooId;
15
var barId: BarId;
16
17
// Safety!
18
fooId = barId; // error
19
barId = fooId; // error
20
fooId = <FooId>barId; // error
21
barId = <BarId>fooId; // error
22
23
// Newing up
24
fooId = 'foo' as any;
25
barId = 'bar' as any;
26
27
// If you need the base string
28
var str: string;
29
str = fooId as any;
30
str = barId as any;
Copied!
最終更新 1yr ago