如何使用接口作为参数键入方法
How to type a method with an interface as parameter
我正在尝试定义一个方法签名,其中参数应符合接口。
interface Command {}
class HelloCommand implements Command {
constructor(public readonly name: string) {}
}
type HandleCommandFn = (command: Command) => Promise<any>;
const handleHello: HandleCommandFn = (
command: HelloCommand
): Promise<string> => {
return Promise.resolve(`Hello ${command.name}`);
};
我从编译器中得到以下错误:
src/index.ts:9:7 - error TS2322: Type '(command: HelloCommand) => Promise<string>' is not assignable to type 'HandleCommandFn'.
Types of parameters 'command' and 'command' are incompatible.
Property 'name' is missing in type 'Command' but required in type 'HelloCommand'.
9 const handleHello: HandleCommandFn = (
我还尝试了以下变体:
type HandleCommandFn = <C extends Command>(command: C) => Promise<any>;
type HandleCommandFn = <C extends Command = Command>(command: C) => Promise<any>;
更新
借助下面的答案,我设法得到了以下代码(我的目标是定义一个类型安全的方法装饰器):
interface Command {
getDescription(): string;
}
type HandleCommandFn<C extends Command = Command> = (
command: C
) => Promise<any>;
function HandleCommand<C extends Command = Command>(
target: Object,
propertyKey: string | symbol,
descriptor: TypedPropertyDescriptor<HandleCommandFn<C>>
): void {
const method = descriptor.value;
if (method) {
// Do something
}
}
class HelloCommand implements Command {
constructor(public readonly name: string) {}
public getDescription(): string {
return "Say hello";
}
}
class HelloCommandHandler {
@HandleCommand // OK
public execute(command: HelloCommand): Promise<string> {
return Promise.resolve(`Hello ${command.name}`);
}
@HandleCommand // Error (wrong parameter type)
public execute2(command: string): Promise<string> {
return Promise.resolve(`Hello ${command}`);
}
@HandleCommand // Error (wrong return type)
public execute3(command: HelloCommand): string {
return `Hello ${command.name}`;
}
}
这应该有效
interface Command {}
class HelloCommand implements Command {
constructor(public readonly name: string) {}
}
type HandleCommandFn<C extends Command = Command> = (
command: C
) => Promise<any>;
const handleHello: HandleCommandFn<HelloCommand> = (
command
): Promise<string> => {
return Promise.resolve(`Hello ${command.name}`);
};
这是因为函数参数是逆变的。
考虑这个例子:
interface Command { }
interface HelloCommand extends Command {
name: string
}
declare var command: Command
declare var helloCommand: HelloCommand
command = helloCommand // ok , is assignable
直觉上,helloCommand
可以分配给 command
因为它更具体。而且很容易理解为什么子类型可以分配给超类型。
但是如果你看这个例子,事情就不再那么明显了:
let commandFn = (command: Command) => void 0
let helloCommandFn = (command: HelloCommand) => void 0
commandFn=helloCommandFn // error, is not assignable anymore
具有 HelloCommand
参数(Command 的子类型)的 FUnction 不再可分配给具有 Command
(超类型)的函数。
这就是逆变的工作原理。继承的方向发生了变化是相反的方向。
你试图做的是不安全的,或者换句话说 - unsound
行为。
为了欺骗编译器,您可以添加额外的泛型:
type HandleCommandFn<T extends Command> = <U extends HelloCommand>(command: T & U) => Promise<any>;
// ok
const handleHello: HandleCommandFn<Command> = (
command
): Promise<string> => {
return Promise.resolve(`Hello ${command.name}`);
};
我正在尝试定义一个方法签名,其中参数应符合接口。
interface Command {}
class HelloCommand implements Command {
constructor(public readonly name: string) {}
}
type HandleCommandFn = (command: Command) => Promise<any>;
const handleHello: HandleCommandFn = (
command: HelloCommand
): Promise<string> => {
return Promise.resolve(`Hello ${command.name}`);
};
我从编译器中得到以下错误:
src/index.ts:9:7 - error TS2322: Type '(command: HelloCommand) => Promise<string>' is not assignable to type 'HandleCommandFn'.
Types of parameters 'command' and 'command' are incompatible.
Property 'name' is missing in type 'Command' but required in type 'HelloCommand'.
9 const handleHello: HandleCommandFn = (
我还尝试了以下变体:
type HandleCommandFn = <C extends Command>(command: C) => Promise<any>;
type HandleCommandFn = <C extends Command = Command>(command: C) => Promise<any>;
更新
借助下面的答案,我设法得到了以下代码(我的目标是定义一个类型安全的方法装饰器):
interface Command {
getDescription(): string;
}
type HandleCommandFn<C extends Command = Command> = (
command: C
) => Promise<any>;
function HandleCommand<C extends Command = Command>(
target: Object,
propertyKey: string | symbol,
descriptor: TypedPropertyDescriptor<HandleCommandFn<C>>
): void {
const method = descriptor.value;
if (method) {
// Do something
}
}
class HelloCommand implements Command {
constructor(public readonly name: string) {}
public getDescription(): string {
return "Say hello";
}
}
class HelloCommandHandler {
@HandleCommand // OK
public execute(command: HelloCommand): Promise<string> {
return Promise.resolve(`Hello ${command.name}`);
}
@HandleCommand // Error (wrong parameter type)
public execute2(command: string): Promise<string> {
return Promise.resolve(`Hello ${command}`);
}
@HandleCommand // Error (wrong return type)
public execute3(command: HelloCommand): string {
return `Hello ${command.name}`;
}
}
这应该有效
interface Command {}
class HelloCommand implements Command {
constructor(public readonly name: string) {}
}
type HandleCommandFn<C extends Command = Command> = (
command: C
) => Promise<any>;
const handleHello: HandleCommandFn<HelloCommand> = (
command
): Promise<string> => {
return Promise.resolve(`Hello ${command.name}`);
};
这是因为函数参数是逆变的。 考虑这个例子:
interface Command { }
interface HelloCommand extends Command {
name: string
}
declare var command: Command
declare var helloCommand: HelloCommand
command = helloCommand // ok , is assignable
直觉上,helloCommand
可以分配给 command
因为它更具体。而且很容易理解为什么子类型可以分配给超类型。
但是如果你看这个例子,事情就不再那么明显了:
let commandFn = (command: Command) => void 0
let helloCommandFn = (command: HelloCommand) => void 0
commandFn=helloCommandFn // error, is not assignable anymore
具有 HelloCommand
参数(Command 的子类型)的 FUnction 不再可分配给具有 Command
(超类型)的函数。
这就是逆变的工作原理。继承的方向发生了变化是相反的方向。
你试图做的是不安全的,或者换句话说 - unsound
行为。
为了欺骗编译器,您可以添加额外的泛型:
type HandleCommandFn<T extends Command> = <U extends HelloCommand>(command: T & U) => Promise<any>;
// ok
const handleHello: HandleCommandFn<Command> = (
command
): Promise<string> => {
return Promise.resolve(`Hello ${command.name}`);
};