【问题标题】:Typescript compiler allows JSON object to be assigned to typed class variableTypescript 编译器允许将 JSON 对象分配给类型化的类变量
【发布时间】:2021-10-21 12:42:19
【问题描述】:

我看到一个案例,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.

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

【问题讨论】:

  • 从 nextwork 中检索 JSON 时,您没有具体的类型。如果你断言你的 JSON 类型不正确,那么所有的赌注都没有了。为避免这种情况,您可以创建类型保护以确保您的数据是正确的形状,或者在极端情况下,使用验证器(例如 github.com/ajv-validator/ajv )来确保 100% 稳健。
  • 签名为&lt;T extends Foo&gt;()=&gt;T 的函数(如您的getIt())总是会出现问题。当调用者在运行时不传递任何参数时,你怎么可能安全地实现声称返回调用者想要的任何Foo 子类型的东西?您几乎需要使用 type assertionthe any type 来实现编译,然后您故意不安全。
  • 你说Bar is different from Foo, and does not implement/extend Foo 但这不是真的。 Bar 确实实现了 Foo;只是没有声明这样做。 TypeScript 的类型系统是structural,不是名义上的。
  • The compiler emits an error if I try to do this because the type is different from the Foo interface. 也不完全正确。 { a: 'a', b: 0, c: true }Foo 100% 兼容,但编译器有一个类似 linter 的规则,称为 excess property checking,如果您将具有额外属性的 object literal 分配给一个变量或属性会忽略它们。
  • 我...认为示例代码和代码 cmets 中发生了很多事情,所以如果没有很多关于 TS 工作原理的解释,我不确定如何回答这个问题。也许您可以减少帖子的数量,只提出一个特定的问题,我们可以回答它?

标签: typescript generics compiler-errors


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

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

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

要表示您的 JSON 将至少解码为 Foo 的属性,您应该简单地返回 Foo 类型。在您的非硬编码示例中,正如 jcalz 在问题 cmets 中指出的那样,您无需担心 excess property checking;你总是可以在你有as T的地方临时添加as Foo,这比as T和通用安全得多。

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

【讨论】:

    猜你喜欢
    • 2017-09-08
    • 2015-05-29
    • 1970-01-01
    • 2018-01-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多