【问题标题】:TypeScript mapped types inference not working as expectedTypeScript 映射类型推断未按预期工作
【发布时间】:2019-09-04 02:23:07
【问题描述】:

给定这个函数:

export const combineValidators = <Input extends { [P in keyof Input]: (val: string) => Err }, Err>(
  validators: Input
) => (values: { [P in keyof Input]?: unknown }): { [P in keyof Input]: Err } => {
  // Ignore implementation.
  return {} as { [P in keyof Input]: Err };
};

还有这个用法:

const validator = combineValidators({
  name: (val) => val ? undefined : 'error',
  email: (val) => val ? undefined : 'error'
});

const errors = validator({
  name: 'Lewis',
  email: 'lewis@mercedes.com'
});

我希望 TypeScript 能够将返回类型推断为:

// Expected: `errors` to be inferred as:
interface Ret {
  name: string | undefined;
  email: string | undefined;
}

但是推断为:

// Actual: `errors` inferred as:
interface Ret {
  name: {};
  email: {};
}

我创建了一个 live example in the TypeScript playground 来演示这个问题。

有人可以帮忙吗?

【问题讨论】:

    标签: javascript typescript


    【解决方案1】:

    Err 不会以您期望的方式推断出来。使用ReturnType 条件类型从Input 中提取返回类型可能更简单:

    type ReturnTypes<T extends Record<keyof T, (...a: any[]) => any>> = {
      [P in keyof T]: ReturnType<T[P]>
    }
    
    export const combineValidators = <Input extends Record<keyof Input, (val: unknown) => any>>(
      validators: Input
    ) => (values: Record<keyof Input, unknown>): ReturnTypes<Input> => {
      return {} as ReturnTypes<Input>;
    };
    
    const validator = combineValidators({
      name: (val) => val ? undefined : 'error',
      email: (val) => val ? undefined : 'error'
    });
    
    const errors = validator({
      name: 'Lewis',
      email: 'lewis@mercedes.com'
    });
    

    我们甚至可以更进一步,如果您在验证器函数中指定参数类型,您可以对传递给validator 的对象的字段进行类型检查:

    type ParamTypes<T extends Record<keyof T, (a: any) => any>> = {
      [P in keyof T]: Parameters<T[P]>[0]
    }
    
    type ReturnTypes<T extends Record<keyof T, (...a: any[]) => any>> = {
      [P in keyof T]: ReturnType<T[P]>
    }
    
    export const combineValidators = <Input extends Record<keyof Input, (val: unknown) => any>>(
      validators: Input
    ) => (values: ParamTypes<Input>): ReturnTypes<Input> => {
      return {} as ReturnTypes<Input>;
    };
    
    const validator = combineValidators({
      name: (val: string) => val ? undefined : 'error',
      email: (val) => val ? undefined : 'error', // if we leave it out, we still get unknown
      age: (val: number) => val ? undefined : 'error'
    });
    
    const errors = validator({
      name: 'Lewis',
      email: 'lewis@mercedes.com',
      age: 0
    });
    
    const errors2 = validator({
      name: 'Lewis',
      email: 'lewis@mercedes.com',
      age: "0" // type error
    });
    

    【讨论】:

    • 太棒了!我需要在 TypeScript 上做得更好。唯一的问题是ReturnType 似乎忽略了undefined;我原以为ReturnType&lt;() =&gt; string | undefined&gt; 会导致string | undefined
    • @riscarrott 你确定你有严格的空检查吗?
    • 令人惊讶的是,我没有,在严格的空检查下完美工作,谢谢!
    猜你喜欢
    • 2021-12-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-28
    • 1970-01-01
    • 2013-02-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多