2016-04-15 22 views
6

Perché i seguenti lavori asserzione:Perché dattiloscritto affermazione letterale oggetto `{a}` opera con interfaccia `{a, b}` ma non `{a} ?, b`

interface AllRequired { 
    a: string; 
    b: string; 
} 

let all = {a: "foo"} as AllRequired; // No error 

Ma questa affermazione dà un errore:

interface SomeOptional { 
    a?: string; 
    b: string; 
} 

let some = {a: "foo"} as SomeOptional; // Error: Property 'b' missing 

L'unica differenza che posso vedere è fare una delle proprietà di interfaccia opzionali (?). Sembra che se tutte le proprietà non sono opzionali, posso affermare un oggetto parziale all'interfaccia, ma non appena una qualsiasi delle proprietà dell'interfaccia è opzionale, non posso più affermare un oggetto parziale. Questo non ha davvero senso per me e non sono stato in grado di trovare una spiegazione di questo comportamento. Cosa sta succedendo qui?


per il contesto: ho encountered this behavior durante il tentativo di aggirare il problema che reagiscono di setState() prende un oggetto di stato parziale, ma TypeScript doesn't yet have partial types per fare questo lavoro correttamente con l'interfaccia dello stato. Come soluzione alternativa, ho trovato il codice setState({a: "a"} as MyState) e ho trovato che funziona finché i campi dell'interfaccia MyState sono tutti non facoltativi, ma non riescono appena alcune proprietà sono facoltative. (Rendere tutte le proprietà facoltative è una soluzione, ma molto indesiderabile nel mio caso.)

+0

ad essere sincero, credo che ci sia solo una persona su StackOverflow in grado di rispondere a quella qu estion e questo è @RyanCavanaugh (si spera che lo convochi). Altrimenti potrebbe esserci un problema nella pagina github di TypeScript. – toskv

risposta

1

Le asserzioni di tipo possono essere utilizzate solo per la conversione tra un tipo e un sottotipo di esso.

Diciamo che ha dichiarato le seguenti variabili:

declare var foo: number | string; 
declare var bar: number; 

Nota number è un sottotipo di number | string, che significa qualsiasi valore che corrisponde al tipo di number (ad esempio 3) corrisponde anche number | string. Pertanto, è consentito utilizzare tipo asserzioni per la conversione tra questi tipi:

bar = foo as number; /* convert to subtype */ 
foo = bar as number | string; /* convert to supertype (assertion not necessary but allowed) */ 

Analogamente, { a: string, b: string } è un sottotipo di { a: string }. Qualsiasi valore corrispondente a { a: string, b: string } (ad es.{ a: "A", b: "B" }) corrisponde anche a { a: string }, perché ha una proprietà a di tipo string.

Al contrario, nessuno di { a?: string, b: string } o { a: string } è un sottotipo dell'altro. Alcuni valori (ad esempio { b: "B" }) corrispondono solo al primo e altri (ad esempio { a: "A" }) corrispondono solo a quest'ultimo.

Se davvero necessità per la conversione tra i tipi non correlati, è possibile aggirare questo utilizzando un supertipo comune (come ad esempio any) come intermedio:

let foo = ({ a: "A" } as any) as { a?: string, b: string }; 

La parte rilevante nel spec è 4.16 Type Assertions:

In a type assertion expression of the form < T > e, e is contextually typed (section 4.23) by T and the resulting type of e is required to be assignable to T, or T is required to be assignable to the widened form of the resulting type of e, or otherwise a compile-time error occurs.

0

Bene, questa è solo una teoria ma ho del codice per eseguirne il backup.

Nel tuo primo esempio, quando esegui il casting su AllRequired stai dicendo al compilatore che sai cosa stai facendo e in seguito imposterai il valore per all.b.

Nel secondo esempio si pensa di fare lo stesso, ma il compilatore probabilmente pensa "hey, se imposta la proprietà opzionale deve impostare un oggetto completo" e quindi è confuso sul motivo per cui il valore per some.b è mancante da questo oggetto completo.

Questo può sembrare un po 'esagerato, ma qui:

let some2 = (all.a ? {a: "foo"} : {}) as SomeOptional 

L'errore è andato se si utilizza una condizione perché poi (beh, probabilmente) some2.a è veramente opzionale.

Provalo in playground.

+0

Beh, l'asserzione fallisce se si passa anche un partial richiesto, ex 'interface {a?: String, b: string, c: string}' non può asserire '{b:" hi "}', ma se make ' a' non facoltativo (quindi * tutti * sono obbligatori), quindi la * stessa * asserzione funziona. È come se la semplice presenza di un campo opzionale assegni l'asserzione a un * controllo * più severo. – Aaron

+0

Non so cosa intendi, il tuo esempio funziona senza errori (assegnando '{b:" hi "}' non ha un errore), ma se aggiungo 'a:" yo "' allora c'è l'errore di nuovo, che può essere spiegato con quello che ho proposto. –

+0

Hai ragione, ho pensato che '{b} come {a ?, b, c}' non funziona, ma in effetti non funziona se include un parametro opzionale, come '{a, b } come {a ?, b, c} ', ma' {a, b} come {a, b, c} 'funziona. Quindi l'aspetto comune è fornire una proprietà * opzionale * che fa sì che il compilatore desideri * tutte * le proprietà non opzionali, o qualcosa del genere. Non sono sicuro di capire cosa sta succedendo nel tuo esempio ternario, sembra lasciare che la spazzatura sia completa come '(vero? {A: 123, abc:" pippo "}: {}) come SomeOptional' che è completamente confuso up statement e value ('a' è di tipo errato,' abc' non esiste) ... – Aaron

Problemi correlati