2017-10-30 4 views
1

次のコードを考えてみましょう。具体的なタイプの違いがkindのADTを持っています。これらのクラスのインスタンスを作成するユーティリティメソッドが必要です。 "a" | "b""a"又は"b"に割り当てないようTypescriptは、ディスクリミネータのユニオン文字列型を使用してADT(識別されたユニオンタイプ)のインスタンスを作成します。

interface I { 
    id: number; 
} 

interface A extends I { 
    kind: "a"; 
} 

interface B extends I { 
    kind: "b"; 
} 

type O = A | B; 

function create(id: number, kind: "a" | "b"): O { 
    return { 
     id: 1, 
     kind: kind 
    }; 
} 

明らかに上記のコードはエラーを返します。とにかくこの機能を表現することはありますか?何とかタイプoneOf "a" | "b"を表現するには?

答えて

2

あなたはコードを正しく表現していると思いますし、TypeScriptが有効な割り当てを妨げていると思います。 O

type OPrime = { 
    id: number; 
    kind: "a" | "b" 
} 

と構造的に同一である必要があり、create()はタイプOPrimeの値を返すために、それはOに割り当て可能でなければならないことを、あなたと私には明らかです。悲しいかな、TypeScriptはOPrimeに割り当て可能なものとしてOを見ていますが、その逆はありません。

あなたのための最も簡単な回避策は、型アサーションを使用することです:

function create(id: number, kind: "a" | "b"): O { 
    return { 
     id: 1, 
     kind: kind 
    } as O; // assertion 
} 

タイプのアサーションは、一般的に安全ではありませんが、あなたは、コンパイラよりも多くを知っているときに便利ですが、式の種類について把握することができます。しかし、コンパイラが警告している正当なバグと、コンパイラが有効な式を認識できないという違いを伝えるのは難しいです。この場合、私はあなたが正しいと判断していることをかなり確信しています。コンパイラは誤ったエラーを報告しています。


これは、TypeScriptの場合、intended behaviorです。 OOPrimeは同じですが、コンパイラがこれを認識するためには余分な作業が必要です。 通常これはnot warrantedです。なぜなら、2つの共用体構成要素が同じ名前の単一のプロパティの型によってのみ異なることはまれであるからです。ほとんどの場合(場合によっては実際のおもちゃ以外の使用例でも)、ABのタイプには他のタイプがあります。たとえば:

interface AExtra extends I { 
    kind: "a"; 
    name: string; 
} 

interface BExtra extends I { 
    kind: "b"; 
    age: number; 
} 

type OExtra = AExtra | BExtra; 

は、今では労働組合の値を持つプロパティを持つ単一の非組合型としてOExtraを表現することはできなくなりました。 OExtraPrime考えてみましょう:

type OExtraPrime = { 
    id: number; 
    kind: "a" | "b"; 
    name?: string; 
    age?: number; 
} 

どのOExtraOExtraPrimeに割り当て可能であることは事実ですが、その逆はありません。したがって、一般的なプラクティスでは、OOPrimeの間の同等の種類が表示されないので、コンパイラはそれを確立することを煩わしくはありません。だからあなたができるのはタイプアサーションです。

希望します。がんばろう!

0

overload declarationsでこのような関数を入力することは可能です。オーバーロードの1つとして望ましいタイプを持つことができ、他のタイプは、kind引数が静的に知られているときに、より具体的な戻りタイプを宣言しています。オーバーロードされた関数の実装における実際の戻り値の型はずっと少なく(実装呼び出しの型チェックにのみ使用されます)、それはちょうどanyでもかまいませんし、実装と互換性のある任意の便利な型でもかまいません宣言

interface I { 
    id: number; 
} 

interface A extends I { 
    kind: "a"; 
} 

interface B extends I { 
    kind: "b"; 
} 

type O = A | B; 


function create(id: number, kind: "a"): A; 
function create(id: number, kind: "b"): B; 
function create(id: number, kind: O["kind"]): O; 
function create<K extends O["kind"]>(id: number, kind: K): { id: number; kind: K } { 
    return { 
     id, 
     kind 
    }; 
} 

function testA(id: number) { // inferred as function testA(id: number): A 
    return create(id, "a"); 
} 
function test(id: number, kind: "a" | "b") { 
// inferred as function test(id: number, kind: "a" | "b"): O 
    return create(id, kind); 
} 
関連する問題