【问题标题】:TypeScript - Weak Types leads to Index Signatures, leading to poor type checkingTypeScript - 弱类型导致索引签名,导致类型检查不佳
【发布时间】:2017-10-06 12:21:22
【问题描述】:

与 AngularJs 的使用相比,索引签名的使用更令人担忧,但我认为这是一个关键问题,需要通过 TypeScript 类型以某种方式解决。

随着 TypeScript 2.4 的弱类型添加,@types/angularjs IController 开始抱怨弱类型,修复(此时唯一可能的修复)是添加索引签名,如此处所引用:

https://github.com/DefinitelyTyped/DefinitelyTyped/issues/17257 https://github.com/DefinitelyTyped/DefinitelyTyped/pull/17303

但在此更改之前,TypeScript 错误将有助于确保当您使用对象中的属性键入 IController 时,它会(适当地)抱怨您错过了定义属性,这是使 TypeScript 成为很棒的语言(至少是 IMO)。

这是一个使用简化的 AngularJs 1.5 组件的直接示例:

let MyComponent = function(): angular.IComponentOptions {
  return {
    bindings: {},
    controller: MyController,
    controllerAs: 'vm',
  }
}

interface IController extends angular.IController {
  x: string;
}

function MyController(this: IController) {
  let vm = this;
  vm.x = "foo"; // OK
  vm.x = 1; // ERROR: As expected due to the definition - great

  // This next line would have complained before this, 
  // now it will let it thru unscathed, same with functions, 
  // arrays, etc. - this is the problem
  vm.y = "bar"; // OK now, ERROR before
}

有没有办法既允许 AngularJs 的类型避免弱类型问题(这是有道理的),同时仍然允许正确检查子类型?

我个人认为,由于这个问题,应该尽可能避免使用索引签名,并且不是避免编译器错误的好方法(尽管目前可能是唯一的方法)。

谢谢

【问题讨论】:

    标签: angularjs typescript


    【解决方案1】:

    我能想出的唯一解决方案是需要更改您的本地角度类型定义文件(或者可以将其推送到上游)。现在 IController 的定义是这样的:

    interface IController {
      $onInit?(): void;
      $doCheck?(): void;
      $onChanges?(onChangesObj: IOnChangesObject): void;
      $onDestroy?(): void;
      $postLink?(): void;
      [s: string]: any;
    }
    

    也许应该改成这样:

    interface IControllerWeak {
      $onInit?(): void;
      $doCheck?(): void;
      $onChanges?(onChangesObj: IOnChangesObject): void;
      $onDestroy?(): void;
      $postLink?(): void;
    }
    
    interface IController extends IControllerWeak {
      [s: string]: any;
    }
    

    这对下游的每个人来说都应该完全相同,但现在它为您提供了对 IControllerWeak 的引用,它只是没有索引签名的 IController。在大多数(所有?)情况下,要求IController 的东西将接受IControllerWeak,反之亦然。

    所以现在您可以使用必需的属性扩展IControllerWeak,并且您有一个具有您想要的保证的非弱(强?)类型:

    interface IController extends angular.IControllerWeak {
      x: string;
    }
    
    function MyController(this: IController) {
      let vm = this;
      vm.x = "foo"; // OK
      vm.x = 1; // ERROR
    
      vm.y = "bar"; // ERROR as desired
    }
    

    IComponentOptions.controller 很乐意接受MyController(或IControllerWeak 子类型的构造函数):

    let MyComponent = function(): angular.IComponentOptions {
      return {
        bindings: {},
        controller: MyController,
        controllerAs: 'vm',
      }
    }
    

    这对你有用吗?

    【讨论】:

    • 很好的相关答案 - 应该可以正常工作!我真的更喜欢不需要覆盖的更好的 TypeScript 语言允许的解决方案,但这肯定会有所帮助。谢谢!
    【解决方案2】:

    你能用映射类型来解决它吗,它可以让你定义一个强大的接口,但是用Partial映射类型让它的属性是可选的...

    // We don't want a weak type... or an index signature...
    interface WeakType {
        x?: string;
    }
    
    interface StrongType {
        x: string;
    }
    
    type PartialStrongType = Partial<StrongType>;
    
    function doSomething(options: PartialStrongType) {
        return options.x || '';
    }
    
    doSomething({}); // OK
    
    doSomething({ x: 'str' }); // OK
    
    doSomething({ x: 1 }); // x can't be a number
    
    doSomething({ y: 'str' }); // hey, you typed 'y', did you mean 'x'
    

    【讨论】:

      猜你喜欢
      • 2022-12-14
      • 1970-01-01
      • 1970-01-01
      • 2019-01-23
      • 2023-03-13
      • 2016-10-11
      • 2021-06-06
      • 2020-10-18
      • 1970-01-01
      相关资源
      最近更新 更多