I am seeing a case where the TypeScript compiler lets me assign a value to a variable that is not the correct type. This results in runtime errors that should get caught at compile time:
// An interface to represent JSON data stored somewhere.
interface Foo {
a: string,
b: number,
}
interface Boo extends Foo {
c: boolean,
}
// A class that has some of the same fields as Foo, but has more stuff going on.
class Bar {
get frank(): string { return this.a + this.b; }
get a(): string { return 'a'; }
get b(): number { return 5; }
greet() { console.log(this.frank, this.a, this.b)};
}
// Some function that retrieves JSON data from where it is stored. Might be Foo or a different data type that extends foo.
function getIt<T extends Foo>() : T {
return { a: 'hi', b: 38 } as T; // Hard coded for the example.
}
// The compiler emits an error if I try to do this because the type is different from the Foo interface.
const notReallyFoo: Foo = { a: 'a', b: 0, c: true };
// The compiler does let me do this even though Bar is different from Foo, and does not implement/extend Foo.
const notReallyBar: Bar = getIt();
notReallyBar.greet(); // Runtime error because the object does not have the greet method.
Is there some change I need to make so errors like this will be caught at compile-time? Is there a better way to approach this?
<T extends Foo>()=>Tlike yourgetIt()is always going to be problematic. How could you possibly safely implement something that claims to return any subtype ofFoothe caller wants when the caller doesn't pass any arguments in at runtime? You're almost required to implement with a type assertion or theanytype to get it to compile, and then you're being unsafe intentionally.Bar is different from Foo, and does not implement/extend Foobut this is not true.Bardoes indeed implementFoo; it's just not declared to do so. TypeScript's type system is structural, not nominal.The compiler emits an error if I try to do this because the type is different from the Foo interface.is also not true exactly.{ a: 'a', b: 0, c: true }is 100% compatible withFoo, but the compiler has a linter-like rule called excess property checking that warns you if you assign an object literal with extra properties to a variable or property that will ignore them.