如何根据另一个参数中的 属性 的值设置类型?
How to set types based on a value of a property in another parameter?
如何根据方法中另一个参数的 属性 值设置类型?我对 TypeScript 比较陌生。
这是我试过的:
class SomeClass {
// constructor, other properties, etc
listen<K extends keyof ClientEvents>({ event: K }: ClientEvent, listener: (...args: ClientEvents[K]) => any)
}
在这里,ClientEvents
看起来像这样:
interface ClientEvents {
"event-1": [data: number]
"event-2": [data: number]
"event-3": [data: any]
}
而ClientEvent
(没有s)看起来像这样:
interface ClientEvent {
event: keyof ClientEvents
anotherProperty: any // I don't want to share all the properties
}
目前,event
字段 会 自动填充到 event-1
,但函数的第一个参数显示为 any
类型,但它应该是 number
类型。如何在不需要更多参数的情况下解决此问题?
将 ClientEvents
内部的 data
之一更改为类型 string
时,它显示为类型 string | number
。
class SomeClass {
listen<K extends keyof ClientEvents>({ event: K }: ClientEvent, listener: (...args: ClientEvents[K]) => any): void;
}
interface ClientEvents {
"event-1": [data: number]
"event-2": [data: string]
}
interface ClientEvent {
event: keyof ClientEvents
anotherProperty: any
}
new SomeClass().listen({
event: "event-1",
anotherProperty: 1
}, (arg) => {
arg // type is string | number
})
这是一个Playground Link
在界面中使用类型参数的解决方法
export interface ClientEvent<L = keyof ClientEvents> {
event: L,
site: string
}
export class SomeClass {
listen<K extends keyof ClientEvents>({ event }: ClientEvent<K>, listener: (...args: ClientEvents[K]) => any): SomeClass
}
和 JavaScript 代码:
let a = new Client()
a.listen({
event: "event-2"
}, (a) => {
a
})
说 a
是一个字符串,所以它工作得很好。
请参阅 the FAQ entry on destructuring assignment and type annotations 以获得可能的权威答案。
您的问题是 { event: K }: ClientEvent
中的 K
不是您认为的那样。在 JavaScript 中,如果你有一个像 { event: K }
这样的函数参数,你正在使用 destructuring assignment with variable renaming,所以你将把那个函数参数的 event
属性 复制到一个变量名为 K
:
function foo({event: K}: ClientEvent) {
// variable -----> ^
console.log(K.toUpperCase());
// ^ <---- see?
}
foo({event: "event-1", anotherProperty: 123}) // EVENT-1
因为 JavaScript 支持这个,所以 TypeScript 也支持(正如你在 ClientEvent
type annotation 中看到的),因此调用签名本身只使用 K
作为它忽略的虚拟参数名称。
当然,K
不能用作同名类型参数 K
的推理站点,因为它们不相关。
您试图将 K
用作 类型注释 event
属性。但这不受支持。事实上,目前还没有办法将类型注释放入解构对象本身。在 microsoft/TypeScript#29526 有一个开放的功能请求要求一些方法来做到这一点(也许......双冒号?像 {event::K}
)但现在这是不可能的。
如果你想给event
属性一个类型,你必须在实际的类型注解中,在解构对象之后做:
{ event }: { event: K }
如果您想保留整个事物需要 ClientEvent
的事实,您需要用 intersection type
之类的东西来表达这一点
{ event }: { event: K } & ClientEvent
或者可能通过扩展您的 ClientEvent
界面(如果看起来更好的话):
interface ClientEventFor<K extends keyof ClientEvents> extends ClientEvent {
event: K
}
declare class SomeClass {
listen<K extends keyof ClientEvents>(
{ event }: ClientEventFor<K>,
listener: (...args: ClientEvents[K]) => any): void;
}
然后事情就会开始为你工作:
new SomeClass().listen(
{ event: "event-1", anotherProperty: 1 },
(arg) => { arg.toFixed() } // okay, arg is number here
)
如何根据方法中另一个参数的 属性 值设置类型?我对 TypeScript 比较陌生。
这是我试过的:
class SomeClass {
// constructor, other properties, etc
listen<K extends keyof ClientEvents>({ event: K }: ClientEvent, listener: (...args: ClientEvents[K]) => any)
}
在这里,ClientEvents
看起来像这样:
interface ClientEvents {
"event-1": [data: number]
"event-2": [data: number]
"event-3": [data: any]
}
而ClientEvent
(没有s)看起来像这样:
interface ClientEvent {
event: keyof ClientEvents
anotherProperty: any // I don't want to share all the properties
}
目前,event
字段 会 自动填充到 event-1
,但函数的第一个参数显示为 any
类型,但它应该是 number
类型。如何在不需要更多参数的情况下解决此问题?
将 ClientEvents
内部的 data
之一更改为类型 string
时,它显示为类型 string | number
。
class SomeClass {
listen<K extends keyof ClientEvents>({ event: K }: ClientEvent, listener: (...args: ClientEvents[K]) => any): void;
}
interface ClientEvents {
"event-1": [data: number]
"event-2": [data: string]
}
interface ClientEvent {
event: keyof ClientEvents
anotherProperty: any
}
new SomeClass().listen({
event: "event-1",
anotherProperty: 1
}, (arg) => {
arg // type is string | number
})
这是一个Playground Link
在界面中使用类型参数的解决方法
export interface ClientEvent<L = keyof ClientEvents> {
event: L,
site: string
}
export class SomeClass {
listen<K extends keyof ClientEvents>({ event }: ClientEvent<K>, listener: (...args: ClientEvents[K]) => any): SomeClass
}
和 JavaScript 代码:
let a = new Client()
a.listen({
event: "event-2"
}, (a) => {
a
})
说 a
是一个字符串,所以它工作得很好。
请参阅 the FAQ entry on destructuring assignment and type annotations 以获得可能的权威答案。
您的问题是 { event: K }: ClientEvent
中的 K
不是您认为的那样。在 JavaScript 中,如果你有一个像 { event: K }
这样的函数参数,你正在使用 destructuring assignment with variable renaming,所以你将把那个函数参数的 event
属性 复制到一个变量名为 K
:
function foo({event: K}: ClientEvent) {
// variable -----> ^
console.log(K.toUpperCase());
// ^ <---- see?
}
foo({event: "event-1", anotherProperty: 123}) // EVENT-1
因为 JavaScript 支持这个,所以 TypeScript 也支持(正如你在 ClientEvent
type annotation 中看到的),因此调用签名本身只使用 K
作为它忽略的虚拟参数名称。
当然,K
不能用作同名类型参数 K
的推理站点,因为它们不相关。
您试图将 K
用作 类型注释 event
属性。但这不受支持。事实上,目前还没有办法将类型注释放入解构对象本身。在 microsoft/TypeScript#29526 有一个开放的功能请求要求一些方法来做到这一点(也许......双冒号?像 {event::K}
)但现在这是不可能的。
如果你想给event
属性一个类型,你必须在实际的类型注解中,在解构对象之后做:
{ event }: { event: K }
如果您想保留整个事物需要 ClientEvent
的事实,您需要用 intersection type
{ event }: { event: K } & ClientEvent
或者可能通过扩展您的 ClientEvent
界面(如果看起来更好的话):
interface ClientEventFor<K extends keyof ClientEvents> extends ClientEvent {
event: K
}
declare class SomeClass {
listen<K extends keyof ClientEvents>(
{ event }: ClientEventFor<K>,
listener: (...args: ClientEvents[K]) => any): void;
}
然后事情就会开始为你工作:
new SomeClass().listen(
{ event: "event-1", anotherProperty: 1 },
(arg) => { arg.toFixed() } // okay, arg is number here
)