Typescript接口泛型扩展

时间:2017-10-27 12:59:41

标签: typescript generics interface

给出以下界面:

interface IMyInterface<T extends { [key: string]: number }> {
    target: T;
    to:  T;
}

我想将其更改为

interface IMyInterface<T extends { [key: string]: number }> {
    target: P extends T;
    to:  T;
}

其中target可以是任何对象,只要它包含T中定义的属性。

示例:

let obj = <IMyInterface>{
    target: { name:"John", age: 100 },
    to: { age: 200 }
}

打字稿编译器应该在以下情况下抱怨:

let obj = <IMyInterface>{
    target: { name:"John", y: 1980 },
    to: { x: 20 }
}

“target”中缺少“x”属性

let obj = <IMyInterface>{
    target: { name:"John", x: "20" },
    to: { x: 30 }
}

类型错误(数字与字符串)

即。我想要对目标进行类型检查,以便它包含“T”泛型中定义的所有属性。

我希望这是一个对象。

创建类似

的方法
methodName<T extends { [key: string]: number }>(target: T, to: T){

}
// and calling it
methodName({ name:"John", x: "20" },{x:30});

即。使用单独的“target”和“to”工作的参数以及Typescript中的类型检查是完美的,但我需要将“target”和“to”包含在一个对象中,以便我可以使用如下方法:

methodName<IMyInterface>(object: IMyInterface){

并将其称为:

methodName({ target: { name:"John", age:20 }, to: { age: 30}});

感谢您的帮助

1 个答案:

答案 0 :(得分:0)

首先说几句:首先,无论何时编写IMyInterface,都需要指定类型参数。像

这样的东西
let obj = <IMyInterface> { // error
    target: { name:"John", age: 100 },
    to: { age: 200 }
}

将始终是一个错误,因为您省略了type参数。这将有效:

let obj = <IMyInterface<{ age: number }>> {
    target: { name:"John", age: 100 },
    to: { age: 200 }
}

或者,更类似于TypeScript,您可以注释变量的类型,而不是声明值的类型:

let obj: IMyInterface<{ age: number }> = {
    target: { name:"John", age: 100 },
    to: { age: 200 }
}

如果IMyInterface的定义指定default type parameter,则可以省略类型参数,但这不会按您希望的方式工作,因为默认值始终相同,而您想要根据to属性的类型而改变的东西。

我的下一个问题是:您是否希望P仅用于检查您是否正确创建了IMyInterface<T>?因此,一旦您创建了有效的IMyInterface<T>,您可以在使用它时立即忘记P?它有点像它。在这种情况下,我要做的是将PIMyInterface<T>的定义中删除,而是使用辅助方法。您注意到一种方法可以帮助您键入检查但是您想要一个对象,对吗?好吧,帮助方法可以返回一个有效的IMyInterface<T>

namespace HelperFunctionSolution {

  // original definition
  interface IMyInterface<T extends { [key: string]: number }> {
    target: T;
    to: T;
  }

  // helper method
  function makeMyInterface<T extends { [key: string]: number }, P extends T>(
    target: P, 
    to: T
  ): IMyInterface<T> {
    return { target, to };
  }

现在您可以使用辅助方法:

  const goodObj = makeMyInterface({ name: "John", age: 100 }, { age: 200 }) // okay

如果您检查goodObj,您会发现它是IMyInterface<{age: number}>。编译器会抱怨你想要的地方:

  const badObj0 = makeMyInterface({}, { x: 30 }) // x is missing
  const badObj1 = makeMyInterface({ name: "John", y: 1980 }, { x: 30 }) 
    // complains about extra property but real problem is x

  const badObj2 = makeMyInterface({ x: "20" }, { x: 30 }) // complains about string 
  const badObj3 = makeMyInterface({ name: "John", x: "20" }, { x: 30 }) 
    // complains about extra property but real problem is x

现在你可以制作你想要的方法了:

  class Hmm {
    methodName<T extends { [key: string]: number }>(target: T, to: T) { /*impl*/ }

等等,这是你的定义,但我认为你想要它采取这样的对象:

    realMethodName<T extends { [key: string]: number }>(
      myInterface: IMyInterface<T>
    ) { /*impl*/ }
  }

并使用它,确保使用辅助函数

  const hmm = new Hmm();
  hmm.realMethodName(makeMyInterface({ name: "John", age: 20 }, { age: 30 })); // okay

}

你不能在那里使用对象文字并保证类型检查,因为只有帮助函数关心P。这是一种方法,但你需要使用辅助函数。

另一种可能性是您在P定义中随身携带IMyInterface类型。这可能不是您想要的,但它具有理想的效果,您可以在methodName方法中使用字符串文字,而不需要辅助函数:

namespace TwoTypeParameterSolution {

  interface IMyInterface<T extends { [key: string]: number }, P extends T> {
    target: P;
    to: T;
  }

  class Hmm {
    realMethodName<T extends { [key: string]: number }, P extends T>(
      myInterface: IMyInterface<T,P>
    ) { /*impl*/ }
  }
  const hmm = new Hmm();
  hmm.realMethodName({target: { name: "John", age: 100 }, to: { age: 200 }}) // okay
  hmm.realMethodName({target: {}, to: { x: 30 }}) // x is missing
  hmm.realMethodName({target: { name: "John", y: 1980 }, to: { x: 30 }}) 
    // complains about extra property but real problem is x

  hmm.realMethodName({target: { x: "20" }, to: { x: 30 }}) // complains about string 
  hmm.realMethodName({target: { name: "John", x: "20" }, to: { x: 30 }}) 
    // complains about extra property but real problem is x

  hmm.realMethodName({ target: { name: "John", age: 20 }, to: { age: 30 } }); // okay
}

无论哪种方式都适合你。希望有所帮助;祝你好运!