给出以下界面:
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}});
感谢您的帮助
答案 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
?它有点像它。在这种情况下,我要做的是将P
从IMyInterface<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
}
无论哪种方式都适合你。希望有所帮助;祝你好运!