Perché le variabili in TypeScript non implicano implicitamente la tipizzazione letterale?

0

Dato un tipo letterale , ad esempio 1 | 2 , l'assegnazione di un valore a una variabile che corrisponde correttamente al tipo letterale avrà esito negativo.

interface SomeInterface {
    foo: (1 | 2);
}
class SomeClass implements SomeInterface {
    foo = 1; // <- generates the following compiler error:
    /*
        Property 'foo' in type 'SomeClass' is not assignable to the same 
        property in base type 'SomeInterface'.
        Type 'number' is not assignable to type '1 | 2'.
    */
}

Tuttavia, dichiarare esplicitamente il tipo letterale soddisferà il compilatore.

interface SomeInterface {
    foo: (1 | 2);
}
class SomeClass implements SomeInterface {
    foo: (1 | 2) = 1; // <- no error
}

È possibile utilizzare anche l'asserzione di tipo:

 
foo = 1 as (1 | 2); // <- no error
// or
foo = <1 | 2>1; // <- no error

Per i tipi primitivi, dovrebbe essere banale per il compilatore sapere che il valore 1 è di tipo (1 | 2) (diamine, anche solo di tipo (1) !). Esiste un motivo di progettazione per cui il compilatore di TypeScript non deduce implicitamente i tipi letterali dai valori letterali?

    
posta Jacob Stamm 25.07.2018 - 21:38
fonte

2 risposte

3

Dal commento di @ amon:

this behaviour indicates that the type checker does not inherit this info from the interface, but first type-checks the class (incl. type inference for foo) and then checks whether the class conforms to the interface. It does not conform because the type of a read-write variable must be invariant.

Sembra che il vero problema qui sia che le classi non inferiscono correttamente i tipi dei loro membri dall'interfaccia (s) implementano . Il problema è il casting non . Se lo fosse, allora foo: (1 | 2) = 1 nell'implementazione class non verrebbe compilato. Il compilatore fa sa che 1 è un valore valido di tipo 1 | 2 , ma il tipo predefinito di 1 è number . Senza il class che conosce il tipo di foo guardando all'interfaccia (che è proprio quello che dovrebbe fare), foo = 1 prova a digitare foo come numero, e in questo momento il interface decide fare il suo lavoro disabilitando la compilazione perché number non è di tipo 1 | 2 ... meglio tardi che mai, suppongo.

L'asserzione di tipo non dovrebbe mai essere usata con i letterali. foo = 3 as (1 | 2) non produce un errore del compilatore, consentendo un errore di battitura accidentale per interrompere un'applicazione, mentre foo: (1 | 2) = 3; non viene compilato, che nella maggior parte dei casi è il comportamento desiderato.

    
risposta data 26.07.2018 - 17:08
fonte
4

Is there a design reason why TypeScript's compiler does not implicitly infer literal types from literal values?

Non puoi farlo in C #:

SomeEnum s = 1; // Fails, even though one of the enum values corresponds to 1.

SomeEnum s = (SomeEnum)1; // succeeds.

In generale, il casting implicito non deve causare la perdita di informazioni. Il cast implicito da, diciamo,% da% a%%% è consentito perché int ha un intervallo molto maggiore di long .

Ma se il tuo linguaggio di programmazione inserisce implicitamente long in int , accetta un tipo (un 1 ) che può essere uno dei 4 miliardi di valori possibili e lo modifica in un tipo che può essere solo uno di due valori possibili. Un cast implicito di questo tipo può facilmente fallire in fase di runtime, se il numero che hai fornito non rientra nell'intervallo.

Se, al contrario, lanci esplicitamente al tipo enum, stai essenzialmente dicendo al compilatore "Permetti al cast, so cosa sto facendo".

    
risposta data 25.07.2018 - 22:02
fonte

Leggi altre domande sui tag