class 实现接口时,打字稿类型推断不正确

Typescript incorrect type inference when class implements interface

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

所以我将 VSCode 与打字稿 3.6.3 一起使用。我已经为 2d 形状创建了接口,它应该有 return 所有法线到边缘的方法。然后我创建 class 来实现该接口,我希望它需要这个方法并且它应该具有相同的 return 类型(这部分有效)和相同的参数类型(这部分无效)。参数被推断为任何。我的问题是我不想手工创建原型链只是为了获得一致的 VSCode 行为。

此外,当 运行 tsc 在控制台中时,我得到相同的错误,因为参数是 'any' 输入 class 方法和内部预期错误访问不存在的 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"
  ]
}

预计:
- class 方法的参数应推断为字符串
实际:
- 方法的参数被推断为任何

最终我的问题如下: "Is this behavior unachievable in ts and I should resort to hand written (oh dear...) prototype chains and simple objects for prototypes?"

提前致谢!

这是 TypeScript 的设计限制(参见 ms/TS#1373). There was a fix attempted at ms/TS#6118 but it had some bad/breaking interactions with existing real-world code, so they gave up on it. There is an open issue at ms/TS#32082 寻求更好的东西,但目前没有任何有用的东西。

此时的建议是在implementing/extending中手动注释参数类型类;这比诉诸手写原型链要好,尽管更烦人。

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

请注意 v 确实可以是 anyunknown 并且 getNormals() 将是正确的实现:

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

这是因为 method parameter contravariance 是类型安全的...WeirdShape 仍然是完全有效的 _IShape2D。因此,虽然将参数推断为 stringnice,但没有什么 incorrect 更通用。

总之,希望对您有所帮助;祝你好运!

Link to code