打字稿:在作为参数传递的函数中键入检查可选参数

时间:2021-01-16 16:04:30

标签: typescript typechecking

代码如下: (问题如下)

一个文件包含这个(types.ts)


declare global{
    interface AnInterface{
    }
}

export type SomeType<T> = 
    T extends [infer A, infer B] ?
      (fst: A, snd:B) => Promise<B> 
        : never

export async function accept<Z extends keyof AnInterface>(aKey: Z, fn : SomeType<Parameters<AnInterface[Z]>>){
    //do sthg with fn
    const firstArg : any = 1;
    const secondArg : any = 2;
    const fnAsAny = <any>fn;
    const result = await fnAsAny(firstArg,secondArg);
    console.log(result)
}

并在另一个文件(“consumer.ts”)中

import {accept} from './types';

declare global{
    interface AnInterface{
        one : typeof fone 
        two : typeof ftwo
        three : typeof fthree
        four : typeof ffour
    }
}

export async function fone(a:number, b:number){return a+b;}
export async function ftwo(a:any, b:string){return a+b;}
export async function fthree(a:string, b:number=1){return b;}
export async function ffour(a:string){}


a: accept("one", fone); // OK
b: accept("two", fone); // ERROR BUT ... is expected: Argument of type '(a: number, b: string) => Promise<string>' is not assignable to parameter of type '(fst: string, snd: string) => Promise<string>'.
c: accept("two", ftwo); // OK
d: accept("three", fthree); // Argument of type '(a: string, b?: number) => Promise<number>' is not assignable to parameter of type 'never'.
e: accept("four", ffour); // Compile Error but would like to pass


我想定义类型 SomeType<T> 以便标记为 d 和 e 的行将通过打字稿检查。 这可能吗?我遇到的问题是可选参数,对于 case e,我可以添加另一个条件类型检查。

打字稿:v 4.1

1 个答案:

答案 0 :(得分:0)

我有一个适用于您的所有情况的简单定义,还有一个关于 ffour 的小警告。

我们想要一个最多接受两个参数的函数。第二个参数可以是可选的,也可以完全省略。该函数返回第二个参数类型的 Promise

type MyFunc<A, B> = (a: A, b: B) => Promise<B>;

在类型本身中,我们声明 B 是必需的。这是因为带有可选参数 extends 的函数带有必需参数,但反之则不然。如果 B 在类型中是可选的,那么我们永远无法使用具有必需的第二个参数的函数来实现该契约。

请注意,参数为 0 的函数会通过,因为参数少于预期的函数是可以的,但不能更多。

我们创建了一个虚拟函数,它什么也不做,看看我们的测试用例是否会被接受为参数。

const checkFunc = <A, B>(func: MyFunc<A, B>): void => {}
// pass with 2 required args
checkFunc(
    async (a: number, b: number) => a + b
);
// pass with 2 requied args of different types
checkFunc( 
    async (a: any, b: string) => a + b
);
// pass with optional second argument
checkFunc(
    async (a: string, b: number = 1) => b
);
// FAIL as expected with more than 2 arguments
checkFunc(
    async (a: number, b: number, c: number) => a + b + c
)

如果没有第二个参数,那么我们的返回类型是什么?它可以是 any -- 因为我们可以使用任何被忽略的第二个参数调用函数 -- 或者它只能是 undefined 吗?这是一个预期行为的问题,因此没有正确或错误的答案。

正如目前所写,单参数函数将传递 any 返回类型,因为第二个参数的值为 unknown

// pass with void function of single argument
checkFunc (
    async (a: string) => {}
)
// pass with function of single argument returning a value
checkFunc (
    async (a: string) => a
)

但是,我们可以更改函数的签名以强制只能返回 void/undefined

// FAIL when returning a value with second argument explicitly disallowed
checkFunc (
    async (a: string, b?: never) => a
)

我们还可以通过显式设置 checkFunc 的泛型而不是允许推断它们来强制失败。

// FAIL based on generics <string, never> -- must have <string, string> to pass
checkFunc<string, never> (
    async (a: string) => a
)

Typescript Playground Link

相关问题