Closed mpfau closed 7 years ago
Types declared with type
are structural, not nominal, so compatibility between them is considered on the basis of their properties. Since you didn't use T
in any of the properties of ClassReference
, T
has no effect on the typing, and the type of loadIt
is effectively loadIt<T>(ref: {id: string}): T
which is a function for which you can treat the return type as anything you want (which is a little nonsensical, but a function that always throw
s would technically fit that type).
Classes are nominal, so if you want T
to have an effect on typing even though it doesn't occur in any properties or methods, use a class instead. This works, for example:
type Vehicle = {
name:string;
}
class ClassReference<T> {
constructor(id: string) {}
}
function loadIt<T>(ref: ClassReference<T>):T {
return ({name: 'Ford'}:any)
}
let VehicleClassRef : ClassReference<Vehicle> = new ClassReference('vehicle')
let loaded = loadIt(VehicleClassRef)
loaded.nonExisting
let loadedAsVehicle: Vehicle = loadIt(VehicleClassRef)
loadedAsVehicle.nonExisting
@jesseschalken Thanks for these detailed insights! We just switched to classes and everything works as expected.
The following example illustrates how generic return type information is lost:
An error should occur at
loaded.nonExisting
. However, the error only occurs at the last line (where the variable has been casted explicitely).