Typescript 编译器允许将 JSON 对象分配给类型化的 class 变量

Typescript compiler allows JSON object to be assigned to typed class variable

我看到 TypeScript 编译器允许我为类型不正确的变量赋值。这会导致应该在编译时捕获的运行时错误:

// 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.

我是否需要进行一些更改以便在编译时捕获此类错误?有没有更好的方法来解决这个问题?

function getIt<T extends Foo>() : T {
    return { a: 'hi', b: 38 } as T; // Hard coded for the example.
}

您似乎打算 getIt 到 return “未知的 Foo 类型扩展,可能是 Boo、Foo 或 Bar”。但是,您在此处描述的语法是“编译器将选择一个适当的类型 T 来扩展 Foo 并相应地填写签名”。自然地,这对编译器来说很难执行——当你甚至不控制 Foo 的类型是什么时,你怎么能真正 return 任何 子类型?——这就是为什么在您的示例中,您的 as T 正在做一些类型不安全的繁重工作。这就是让您下面的行起作用的原因:

// the compiler sets T = Bar, which is "fine" since
// structurally Bar extends Foo
const notReallyBar: Bar = getIt();

要表示您的 JSON 至少会解码为 Foo 的属性,您只需 return 键入 Foo。在您的非硬编码示例中,正如 jcalz 在问题评论中指出的那样,您无需担心 excess property checking;你总是可以在你有 as T 的地方临时添加 as Foo,这比 as T 和通用的要安全得多。

function getIt() : Foo {
    return /* your value here */;
}