[英]can I provide a default value for keyof parameter in typescript?
Trying to enforce an optional, defaulted parameter on a generic method that must be a property of the passed object.尝试在泛型方法上强制使用可选的默认参数,该参数必须是传递对象的属性。 Here is an example:
下面是一个例子:
function doIt<T>(target: T, propA: keyof T, propB: keyof T): string {
return `${target[propA]} ${target[propB]}`;
}
But, in the above example, I would like to default propA
and propB
to foo
and bar
for example:但是,在上面的例子中,我想默认
propA
和propB
为foo
和bar
例如:
function doIt<T>(target: T, propA: keyof T = 'foo', propB: keyof T = 'bar'): string {
return `${target[propA]} ${target[propB]}`;
}
But when I do that, it complains that Type '"bar"' is not assignable to type 'keyof T'.ts(2322)
which makes sense.但是当我这样做时,它抱怨
Type '"bar"' is not assignable to type 'keyof T'.ts(2322)
这是有道理的。 Even if I add a default type to T
, it still doesn't work:即使我向
T
添加默认类型,它仍然不起作用:
function doIt<T = { foo: string; bar: string }>(target: T, propA: keyof T = 'foo', propB: keyof T = 'bar'): string {
return `${target[propA]} ${target[propB]}`;
}
Basically, I want to be able to do:基本上,我希望能够做到:
doIt({foo: 'jane', bar: 'doe'}); // should return 'jane doe'
doIt({zoo: 'jane', baz: 'doe'}); // should fail compilation
doIt({zoo: 'jane', baz: 'doe'}, 'zoo', 'baz'}); // should return 'jane doe'
Any ways of doing this?有没有办法做到这一点?
You can't easily use default parameter values for this without running afoul of the type checker.在不与类型检查器发生冲突的情况下,您不能轻松地为此使用默认参数值。
The simplest way to get what you're looking for is probably to use overloads to describe the different intended ways to call doIt()
, like this:获得所需内容的最简单方法可能是使用重载来描述调用
doIt()
的不同预期方法,如下所示:
namespace Overloads {
function doIt<T>(target: T, propA: keyof T, propB: keyof T): string;
function doIt<T extends { bar: any }>(target: T, propA: keyof T): string;
function doIt<T extends { foo: any; bar: any }>(target: T): string;
function doIt(target: any, propA?: keyof any, propB?: keyof any): string {
if (typeof propA === "undefined") propA = "foo";
if (typeof propB === "undefined") propB = "bar";
return `${target[propA]} ${target[propB]}`;
}
console.log(doIt({ foo: "jane", bar: "doe" })); // jane doe
console.log(doIt({ zoo: "jane", baz: "doe" })); // error!
// ┌─────────────> ~~~~~~~~~~~~
// "zoo" not expected (although I'm surprised it doesn't complain about missing foo/bar)
console.log(doIt({ zoo: "jane", baz: "doe" }, "zoo", "baz")); // jane doe
console.log(doIt({ zoo: "jane", bar: "doe" }, "zoo")); // jane doe
}
That works the way you want.这以您想要的方式工作。 Note that the three call signatures correspond to different numbers of parameters, and they each have different constraints on the generic type parameter
T
.请注意,三个调用签名对应于不同数量的参数,并且它们每个对泛型类型参数
T
都有不同的约束。 (You may wish to use string
instead of any
as property types, there... not sure) (您可能希望使用
string
而不是any
作为属性类型,那里......不确定)
Note that in the implementation signature I've got target
as any
, propA
, and propB
as (keyof any) | undefined
请注意,在实现签名中,我将
target
作为any
、 propA
和propB
作为(keyof any) | undefined
(keyof any) | undefined
. (keyof any) | undefined
。 So the implementation won't be particularly type-safe here.所以这里的实现不会是特别类型安全的。 Overloads don't really help with implementation type safety;
重载并不能真正帮助实现类型安全; they are more for the caller's benefit.
他们更多的是为了呼叫者的利益。 If you tried to make the implementation generic you'd run into similar problems where the compiler can't verify that
"foo"
is assignable to keyof T
, etc.如果您试图使实现通用,您会遇到类似的问题,编译器无法验证
"foo"
是否可分配给keyof T
等。
Also note that we can't use default parameter values in overload signatures, but that's fine... we can just do that assignment ourselves inside the function implementation.还要注意,我们不能在重载签名中使用默认参数值,但这很好……我们可以在函数实现中自己进行赋值。 If the parameters are
undefined
, reassign them.如果参数
undefined
,则重新分配它们。
Another way to do this is to use TypeScript's support for treating rest parameters as tuple types , including optional tuple elements :另一种方法是使用 TypeScript 支持将其余参数视为元组类型,包括可选的元组元素:
namespace ConditionalTupleParameters {
function doIt<T>(
target: T,
...[propA, propB]: T extends { foo: any; bar: any }
? [(keyof T)?, (keyof T)?] // propA and propB are optional
: T extends { bar: any }
? [keyof T, (keyof T)?] // propA is required, propB is optional
: [keyof T, keyof T] // propA and propB are optional
): string {
if (typeof propA === "undefined") propA = "foo";
if (typeof propB === "undefined") propB = "bar";
return `${target[propA]} ${target[propB]}`;
}
console.log(doIt({ foo: "jane", bar: "doe" })); // jane doe
console.log(doIt({ zoo: "jane", baz: "doe" })); // error!
// ┌──────> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// expected 3 arguments, got 1
console.log(doIt({ zoo: "jane", baz: "doe" }, "zoo", "baz")); // jane doe
console.log(doIt({ zoo: "jane", bar: "doe" }, "zoo")); // jane doe
}
This works similarly to the overloads.这与重载类似。 It's fairly ugly looking, which is not great.
它看起来相当丑陋,这不是很好。 But, the implementation is actually relatively type-safe, where
target
is still known to be type T
, and propA
and propB
end up being keyof T
after checking against undefined
.但是,该实现实际上是相对类型安全的,其中
target
仍然已知为类型T
,并且propA
和propB
在检查undefined
后最终成为keyof T
。
Okay, hope one of those helps.好的,希望其中之一有帮助。 Good luck!
祝你好运!
What about casting the default value?铸造默认值怎么样?
Simplified example简化示例
const doIt = <T = { id: string }>(key: keyof T = 'id' as keyof T) => {}
Example from question问题示例
function doIt<T>(target: T, propA: keyof T = 'foo' as keyof T, propB: keyof T = 'bar' as keyof T): string {
return `${target[propA]} ${target[propB]}`;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.