タイプ型がなぜ型でないのか
10104 ワード
一見してnever タイプは、日常コーディングのために非常に役に立ちません.しかし、実際には、型の次のプロパティは非常に便利です.
小さなハックを使用すると、既存のコードを壊したり、ランタイムのオーバーヘッドなしでコードベースの型安全性を高めるために使用できます.
たぶん、あなたはあなたのコードの場所をたくさん持っています.そこでは、ユーザーID、電子メール、firstnameまたは何のようないくつかのデータを使用します.そのための標準型を使うことができます(文字列など).
間違ったパラメータで誤ってこのメソッドを呼ぶことができました.例えば、ユーザIDの代わりにメールで. このエラーは実行時にコンパイル時ではない. 時々refactoring ユーザーIDのようなフィールドを使用するすべての場所を見つけたいコードベース.これは、単純な型が非常に難しいですが、カスタムタイプを持っている場合は非常に簡単です.
組み込みの文字列型を使用しないが、ユーザーIDにカスタム型を作成する場合は、上記の問題がなくなります.
この新しいユーザータイプを実装する方法?単純な解決策はうまくいきません.
文字列値を型の変数に代入することはできません 文字列を新しい型に変換するには、後者をキャストする必要があります.このタイプの値を作成する別の方法は不可能です.通常、あなたのテストでは、URLパラメータの解析中またはREST リソース. 私は通常、鋳造のためのこのような工場の方法を書く 大きな利点は、このトリックがコードの実行時動作を変更しないことです.健全性チェックはコンパイル時のみ行われます.まだだから 文字列だけでなく、他のタイプにも使用できます. では、この型を動作させましょう.
タイプの値が多くの場所で使われるが、少数の場合だけにつくられるならば、私はこのアプローチが特に役に立つとわかりました.例えば、ファクトリ関数toseridを呼び出すだけで、文字列を新しいユーザID型に変換する必要があります. RESTリソースをドメインモデルに変換する(一つの場所でなければなりません) URLパラメータの解析 テストで
しかし、多くの場所があるかもしれません、関数はパラメタとして我々の新しいタイプを評価して、ちょうどそれをまわりに渡します.したがって、このアプローチを通して、私はより豊かなタイプモデルの前者の利点を得ます.
私は、このトリックについてAngular Meetupは2020年にTheele Leonardによって開催された.私は別の機会には、多くのトリックが含まれてホールドを見つけたので、見ている.
[…] The never type is assignable to every type; however, no type is assignable to never (except never itself) […]
小さなハックを使用すると、既存のコードを壊したり、ランタイムのオーバーヘッドなしでコードベースの型安全性を高めるために使用できます.
問題
たぶん、あなたはあなたのコードの場所をたくさん持っています.そこでは、ユーザーID、電子メール、firstnameまたは何のようないくつかのデータを使用します.そのための標準型を使うことができます(文字列など).
getUserByUserId(userId: string): Observable<User>;
しかし、このアプローチにはいくつかの問題があります.解決策
組み込みの文字列型を使用しないが、ユーザーIDにカスタム型を作成する場合は、上記の問題がなくなります.
getUserByUserId(userId: UserId): Observable<User>;
今、このユーザーIDタイプが使用されるすべての場所を見つけるのは簡単です.後者は別のタイプを持っているので、誤ってメールでこのメソッドを呼び出すことはできません.決してタイプは遊びに来ませんか?
この新しいユーザータイプを実装する方法?単純な解決策はうまくいきません.
export type UserId = string;
getUserByUserId('some string'); // no compiler errors here :-(
問題は、typescriptのためですstring
and UserId
互換性があります(実行時に同じであるため).我々は、変更する必要がありますUserId
タイプ(コンパイラコンパイルエラーを得るために少なくとも互換性がない).私たちは新しいUserId
それを達成するクラス.しかし、これは既存のコードを壊すかもしれません.しかし、助けてnever このような新しい型を定義できます.export type UserId = string & {
// name of the following field needs to be unique. If it
// were not unique and you would reuse it for another type,
// both types would be compatible!
____doesNotMatter_UserId: never;
};
// you can't create new values of type UserId by hand, because
// you cannot assign anything to never. so you'll need this
// converter function:
export const toUserId = (userId: string) => userId as UserId;
このソリューションにはいくつかの良い特性があります.UserId
彼らが行方不明であるので、もう____doesNotMatter_UserId
属性.あなたがそれを試してみるならば、コンパイラは文句を言うでしょう.const toUserId = (userId: string) => userId as UserId;
. この工場の方法は良いですので、今どこで場所を検索することができますUserId
値が作成されます.string
ランタイム中に、コード内の場所を移行するのを忘れると、何も悪いことは起こりませんstring
上の例では).export type UserId = string & {
// name of the following field needs to be unique. If it
// were not unique and you would reuse it for another type,
// both types would be compatible!
____doesNotMatter_UserId: never;
};
// you can't create new values of type UserId, so you'll need
// this converter function:
export const toUserId = (userId: string) => userId as UserId;
// an example function which expects our new type
export const isSuperUser = (userId: UserId) =>
userId === 'superuser';
// a function we forgot to migrate and which still get's a string
export const unmigratedIsGuestUserFn = (userId: string)
=> userId === 'guest';
// some examples
const aUserId: UserId = toUserId('u1111');
isSuperUser(aUserId); // works and return false
isSuperUser('superuser'); // Compile time error: Argument of type
// 'string' is not assignable to parameter of type 'UserId'.
// Type 'string' is not assignable to type '{
// ____doesNotMatter_UserId: never; }'.
unmigratedIsGuestUserFn(aUserId); // no compile time errors
// because at runtime our new type is just a string :-).
// So this type refactoring is very safe
// warning - don't reuse your ___doesNotMatter attributes:
export type Email = string & {
// DO NOT DO THIS:
____doesNotMatter_UserId: never;
};
// Email and UserId are compatible because they have the same
// signature, so we don't get compiler errors here:
const email: Email = toUserId('superuser')
isSuperUser(email);
// So -> always use unique names for these
// ____doesNotMatter attributes
結論
タイプの値が多くの場所で使われるが、少数の場合だけにつくられるならば、私はこのアプローチが特に役に立つとわかりました.例えば、ファクトリ関数toseridを呼び出すだけで、文字列を新しいユーザID型に変換する必要があります.
しかし、多くの場所があるかもしれません、関数はパラメタとして我々の新しいタイプを評価して、ちょうどそれをまわりに渡します.したがって、このアプローチを通して、私はより豊かなタイプモデルの前者の利点を得ます.
謝辞
私は、このトリックについてAngular Meetupは2020年にTheele Leonardによって開催された.私は別の機会には、多くのトリックが含まれてホールドを見つけたので、見ている.
Reference
この問題について(タイプ型がなぜ型でないのか), 我々は、より多くの情報をここで見つけました https://dev.to/mschwartau/why-typescripts-never-type-is-super-useful-1pa2テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol