Typescript 我怎样才能有一个“做一个不完整的类型”<;Foo>;但是传递的引用只包括来自Foo的键
假设我有Typescript 我怎样才能有一个“做一个不完整的类型”<;Foo>;但是传递的引用只包括来自Foo的键,typescript,Typescript,假设我有 class Foo { readonly a!: string readonly b!: string readonly c!: string } 然后我创建了一个带有参数的方法 Partial<Foo> 我可以将Bar传递给采用Partial的方法,但这不是我想要的 我想要的是某种类型的AllowOnlyKeys from,您仍然可以有0个键,或1个键,或所有键,但是Bar不匹配,因为它有一个不在foo中的键 我尝试过使用许多库,包括ts ess
class Foo {
readonly a!: string
readonly b!: string
readonly c!: string
}
然后我创建了一个带有参数的方法
Partial<Foo>
我可以将Bar
传递给采用Partial
的方法,但这不是我想要的
我想要的是某种类型的AllowOnlyKeys from
,您仍然可以有0个键,或1个键,或所有键,但是Bar
不匹配,因为它有一个不在foo中的键
我尝试过使用许多库,包括ts essentials
,ts typedefs
,以及ts toolbelt
,但还没有找到解决方案
它们是否有任何方法可以获得我想要的行为,这样编译器就可以防止我将完全错误的对象传递给mymethod(part:Partial)
?您想要的对象,而这些对象在TypeScript中并不存在。TypeScript中的对象类型被认为是“开放的”或“可扩展的”;像{a?:string,b?:string,c?:string}
这样的类型只指定a
,b
和c
键处的属性类型。它根本不禁止使用其他键。所以像{a:,b:,c:,d:12345}
这样的值是有效的。因此,条
是有效的部分
请注意,这通常是一个理想的功能,因为它允许接口
和类
扩展:
interface X {
x: string;
}
interface Y extends X {
y: number;
}
这里,Y扩展X
意味着每个Y
都是一个X
。这直接导致了你不满意的过度房产扩张:
const y: Y = { x: "", y: 1 }
const x: X = y; // okay
有一些近似于精确类型的变通方法,这些方法对于您的目的来说可能表现得足够好,但都可以避免,因为编译器总是允许您将值扩展到不知道有问题的键的类型
我首选的解决方法是在
部分
中创建myMethod()
,类似于部分
的类型T
。您可以T
,这样其中的每个已知属性都需要是Foo
中的属性,如果有任何额外属性,则它们必须是从不
。像这样:
mymethod<T extends { [K in keyof T]: K extends keyof Foo ? Foo[K] : never }>(
part: T
) {
console.log(part);
}
万岁,它阻止了条
!如果您将对象交给mymethod
,这非常好,因为已知这些对象具有多余的属性。但没有什么能阻止这一点:
const partialFoo: Partial<Foo> = bar; // valid assignment
obj.mymethod(partialFoo); // no error!
然后制作一个版本的mymethod()
,在对其进行操作之前,从零件
中提取a
、b
和c
属性:
mySafeMethod(part: Partial<Foo>) {
this.mymethod(pluck(part, "a", "b", "c"));
}
运行时代码期望部分
可能比a
、b
和c
具有更多属性,并显式忽略它们,而不是使用对象.keys()
或for.
迭代所有现有键。这可能不是惯用的JavaScript,但它确实使编译器更容易处理
好的,希望这些想法中的一个能帮助你;祝你好运
更新 TypeScript并不能真正“获取”确切的类型,尤其是未指定的泛型类型,就像您在
mymethod
的实现中看到的那样。因此在mymethod
中,编译器对part
知之甚少。有很多方法可以解决这个问题。。。一种更丑陋的方法是将精确类型与开放类型相交,这样编译器至少可以知道T
是一个部分
以及其他它无法验证的内容:
mymethod<T extends Partial<Foo> &
{ [K in keyof T]: K extends keyof Foo ? Foo[K] : never }
>(
part: T
) {
console.log(part);
part.b?.toUpperCase(); // okay
}
mymethod(
第部分:T
) {
控制台日志(部分);
part.b?.toUpperCase();//好的
}
这对你有用吗
@jcalz的答案很好,但不是我最终实现的,而是它引导我实现的
type FooPart = Opaque<Partial<Foo>>;
在哪里
将失败。可能与此相关。我尝试了顶部答案中的
NoExtraProperties
,但是Partial
类型阻止它按预期工作。该页面上涉及鉴别器属性的其他解决方案可能是您的最佳选择。如果mymethod
出现问题,访问方法内部的属性会导致编译时失败。是的,这是可行的。。。。我得弄清楚如何把那一长串代码变成泛型类型。。。我还有一些其他的问题。。。这可能在内部通过强制转换解决…您可以始终使用重载将调用签名与实现签名分离,如mymethod(part:T):void
用于调用签名,然后mymethod(part:Partial){…}
用于实现
mySafeMethod(part: Partial<Foo>) {
this.mymethod(pluck(part, "a", "b", "c"));
}
obj.mySafeMethod(bar); // { a: "" };
mymethod<T extends Partial<Foo> &
{ [K in keyof T]: K extends keyof Foo ? Foo[K] : never }
>(
part: T
) {
console.log(part);
part.b?.toUpperCase(); // okay
}
type FooPart = Opaque<Partial<Foo>>;
foo(): Foo {
return { a: 'test' } as FooPart;
}
foo(): Foo {
return { a: 'test', d: 'not valid' } as FooPart;
}