Typescript继承并重载了函数的字符串文字参数
我目前正在尝试向一个非常依赖继承的库中添加一些类型。一般层次结构类似于:Typescript继承并重载了函数的字符串文字参数,typescript,Typescript,我目前正在尝试向一个非常依赖继承的库中添加一些类型。一般层次结构类似于: BaseWidget --> TextBox --> ValidationTextBox BaseWidget提供了一个名为getObj(name)的JS函数,该函数映射到TS中的getObj(name:string):any,有效地查找当前对象中名为\u get的函数,执行该函数并返回结果 这些“属性”在各个类上公开,并在类之间继承,以便ValidationTextBox可以访问TextBox上的所有属性。
BaseWidget --> TextBox --> ValidationTextBox
BaseWidget提供了一个名为getObj(name)
的JS函数,该函数映射到TS中的getObj(name:string):any
,有效地查找当前对象中名为\u get
的函数,执行该函数并返回结果
这些“属性”在各个类上公开,并在类之间继承,以便ValidationTextBox可以访问TextBox上的所有属性。我想知道是否可以添加类似于我在下面尝试的类型,而不必在每个类中重新定义重载
interface BaseWidget {
getObj(name: string): any
}
interface TextBox extends BaseWidget {
getObj(name: "B"): string
}
interface ValidationTextBox extends TextBox {
getObj(name: "C"): boolean
// getObj(name: "B"): string; // Adding this would make it compile, but obviously not ideal in the least
// getObj(name: string): any // Adding this also compiles, but I lose type information for 'getObj("B")'
}
declare const obj: ValidationTextBox;
console.log(obj.getObj("B")); // Error: Argument of type '"B"' is not assignable to parameter of type '"C"'
此当前解决方案的错误是接口“ValidationTextBox”错误地扩展了接口“TextBox”。
因为在getObj(…)
中“B”不可分配给“C”
谢谢你的帮助 可能有几种解决方案。最简单的方法是通过指定额外的重载使编译器满意,但不使用类型查询重新编写它们:
interface ValidationTextBox extends TextBox {
// The extra overload with C, and whatever overloads are in the base class (TextBox['getObj'])
getObj: ((name: "C") => boolean) & TextBox['getObj'];
}
另一种解决方案是将类型设置为泛型,以允许我们添加到name
参数的类型中,而不会实际覆盖该方法:
interface BaseWidget<TName = string> {
getObj(name: TName): any
}
// TName can be used to add to the getName overload in derived interfaces
interface TextBox<TName = never> extends BaseWidget<TName | "B"> {
}
// TName can be used to add to the getName overload in derived interfaces
interface ValidationTextBox<TName = never> extends TextBox<TName | "C"> {
}
declare const obj: ValidationTextBox;
console.log(obj.getObj("B"));
console.log(obj.getObj("C"));
interface-BaseWidget{
getObj(名称:TName):任何
}
//TName可用于添加到派生接口中的getName重载中
接口文本框扩展BaseWidget{
}
//TName可用于添加到派生接口中的getName重载中
接口验证TextBox扩展了TextBox{
}
声明常量对象:ValidationTextBox;
console.log(obj.getObj(“B”));
console.log(obj.getObj(“C”));
在尝试了一段时间后,我发现了另一个几乎完美满足我需求的解决方案:
/--Properties接口设置getObj/setObj--//
接口属性{
getObj(名称:U):T[U]
setObj(名称:U,值:T[U]):void;
setObj(map:{[key in U]:T[key]}):void;
}
//---TextBox类使用的一些数据类接口--//
接口值{
a:数字;
}
接口MoreSomeValue扩展了SomeValue{
b:弦;
}
//---定义类的属性----//
接口TextBoxProps{
value:ValueType;//可以(必须?)通过泛型完全更改继承类中的属性类型
禁用:布尔值;
test:SomeValue;//但是重写以返回更具体的值是可以的
}
接口验证TextBoxProps扩展了TextBoxProps{
min:编号;
最大:数量;
测试:更多值
}
//---扩展属性的实际类接口---//
接口文本框扩展了_属性{}
接口验证TextBox扩展了_属性,TextBox{}
//---允许设置属性的构造函数--//
接口验证TextBoxConstructor{
//部分,因为所有属性都是可选的
新增(道具:部分):ValidationTextBox;
}
declare const ValidationTextBoxConstructor:ValidationTextBoxConstructor;//实际建造师
//---用法--//
//以下是编译时检查的所有类型,更改任何道具/字符串/值都将导致编译时错误
const obj=新的ValidationTextBoxConstructor({min:0,max:5,disabled:true});
日志(obj.getObj(“测试”));
对象设置对象(“最小”,5);
setObj({min:5,max:0,disabled:false});
解决方案的核心是_属性接口。在此基础上进行扩展并指定泛型类型T使您可以使用getObj(name:string)
访问,其中name必须是所提供类型的键,并且它将返回类型T[name]。同样适用于setObj(名称:string,值:T[U])
,其中值必须是由T[name]指定的类型。第二个setObj
函数还接受{key=>value}的散列,并对提供的每个键调用setObj(key,value)。该类型还正确地检查传入的对象,方法是允许T的任何键,并使其值的类型为T[key]
*Props接口只定义可用的属性,并用作_属性中的类型。如果使用它们的类从另一个扩展_属性的类扩展,则它们应该相互扩展
这还提供了一个构造函数,可以选择获取属性名并设置它们
唯一的问题是,如果属性需要完全更改其类型(例如,基类具有string属性,但继承类将其公开为数字),但这可以通过Props接口上的泛型解决。这不是最干净的解决方案,但非常罕见,对我的需求来说没有太大影响。难以置信!非常感谢。我不知道在接口中可以使用这样的类型查询。我会再多考虑一下,但我相信第一个选择就是我要寻找的。事实上,我在这些方法中遇到了一些问题。当多个类上有很多属性时,第一个方法会变得有点笨拙(最终在接口属性上有很多类型交集),而泛型方法只允许指定名称,但将返回类型保留为any。我在下面发布了我自己的答案,灵感来自于你在这里提到的通用方法。