【问题标题】:Typescript incorrect type inference when class implements interface类实现接口时打字稿不正确的类型推断
【发布时间】:2020-02-03 22:56:34
【问题描述】:

在 typescript 中使用组合方法而不是继承之一时,我想根据实体“可以”而不是“是”来描述我的实体。为了做到这一点,我需要创建一些复杂的接口,然后为我的类(我使用类是为了不创建手动原型链并且不破坏我认为存在于 js 引擎中的一些优化)来实现我的接口。但是,当没有正确推断出方法的类型时,这会导致奇怪的行为。相反,当使用对象并将它们声明为相同的接口类型时,一切都按预期工作。

所以我正在使用带有 typescript 3.6.3 的 VSCode。我已经为 2d 形状创建了接口,该接口应该具有将所有法线返回到边缘的方法。然后我创建了实现该接口的类,我希望它需要这个方法,它应该具有相同的返回类型(这部分有效)和相同的参数类型(这个没有)。参数被推断为任何。我的问题是我不想仅仅为了获得一致的 VSCode 行为而手动创建原型链。

另外,当在控制台中运行 tsc 时,我得到相同的错误,因为参数在类方法中是“任何”类型,而在访问不存在的 prop 时对象方法内部出现预期错误

interface _IVector2 {
  x: number;
  y: number;
}

interface _IShape2D {
  getNormals: ( v: string ) => _IVector2[];
}

export class Shape2D implements _IShape2D {
  getNormals( v ) {
    console.log( v.g );
                   ^ ----- no error here

    return [{} as _IVector2];
  }
}

export const Test: _IShape2D = {
  getNormals( v ) {
    console.log( v.g );
                   ^------ here we get expected error that  
                   ^------ 'g doesn`t exist on type string'

    return [{} as _IVector2];
  }
};

我的 tsconfig.json

{
  "compilerOptions": {
    "target": "es2017",
    "allowSyntheticDefaultImports": true,
    "checkJs": false,
    "allowJs": true,
    "noEmit": true,
    "baseUrl": ".",
    "moduleResolution": "node",
    "strict": true,
    "strictNullChecks": true,
    "noImplicitAny": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noImplicitReturns": true,
    "noImplicitThis": true,
    "noFallthroughCasesInSwitch": true,
    "jsx": "react",
    "module": "commonjs",
    "alwaysStrict": true,
    "forceConsistentCasingInFileNames": true,
    "esModuleInterop": true,
    "noErrorTruncation": true,
    "removeComments": true,
    "resolveJsonModule": true,
    "sourceMap": true,
    "watch": true,
    "skipLibCheck": true,
    "paths": {
      "@s/*": ["./src/*"],
      "@i/*": ["./src/internal/*"]
    }
  },
  "exclude": [
    "node_modules"
  ]
}

预期:
- 类方法的参数应该被推断为字符串
实际:
- 方法的参数被推断为any

最后我的问题如下: “这种行为在 ts 中是无法实现的吗?我应该求助于手写(哦,亲爱的……)原型链和原型的简单对象?”

提前谢谢你!

【问题讨论】:

  • 一个方法的签名由它的名字和参数组成,所以打字稿只有在你指定相同的参数类型(在你的情况下是字符串)时才会知道你正在从接口实现方法。跨度>
  • 它实际上可以正确推断返回类型,即使我没有指定任何参数。那么根据这个能力,为什么它不“理解”应该是什么参数类型呢?

标签: typescript class methods interface implements


【解决方案1】:

这是 TypeScript 中的 design limitation。有一个fix attempted,但它与现有的现实世界代码有一些不良/破坏性的交互,所以他们gave up on it

此时的建议是在实现/扩展类中手动注解参数类型;这比求助于手写原型链要好,尽管更烦人。

export class Shape2D implements _IShape2D {
  getNormals(v: string) { // annotate here
    console.log(v.g); // <-- error here as expected
    return [{} as _IVector2];
  }
}

请注意,v 确实可以是 anyunknowngetNormals() 将是正确的实现:

export class WeirdShape implements _IShape2D {
  getNormals(v: unknown) { // okay
    return [];
  }
}

这是因为method parameter contravariance 是类型安全的...WeirdShape 仍然是完全有效的_IShape2D。因此,虽然将参数推断为string很好,但没有什么不正确关于它更通用。

无论如何,希望对您有所帮助;祝你好运!

Link to code

【讨论】:

  • 感谢您的回复。我有一些想法,我不可能是第一个发现这种“错误”的人,你的回答很好地解释了。但我想确保我做的一切都是正确的。这个用例在推断类实例的方法类型时存在一些问题,但是当我们处理具有恰好是函数的属性的具体对象时就没有这样的问题。为什么会这样?另外,我不确定如果我明确地说该参数是字符串,那么应该允许将其设置为 any。其他方式 - 当然,但不像你描述的那样
  • 上下文类型发生在显式注释的对象上,但不会发生在类方法上,因为后者从未实现过,而且当他们尝试时,它会破坏人们的代码太多。我认为这只是历史上的侥幸,而不是原则上的差异。仍然有some hope 这将被修复。
  • this
猜你喜欢
  • 1970-01-01
  • 2021-05-06
  • 2018-08-12
  • 2018-06-09
  • 1970-01-01
  • 1970-01-01
  • 2020-04-02
  • 2020-12-20
  • 2016-08-03
相关资源
最近更新 更多