在这个场景下如何准确判断类型?

问题描述 投票:0回答:1

考虑函数 f,其定义如下:

function f<T extends Fields = Fields>(props: Props<T>) {
  return null;
}

在该函数中,T代表扩展Fields的泛型类型,Fields定义为:

export type Fields = { [key: string]: unknown };

此外,Props接口定义为:

export interface Props<T extends Fields = Fields> {
  fields: Config<T>;
  onSubmit?: (values: Values<T>) => void;
}

这里,Props 接受扩展 Fields 的泛型类型 T,它由两个属性组成:fields 和 onSubmit。 fields 属性的类型为 Config,onSubmit 属性是一个可选函数,它采用 Values 类型的值并返回 void。

为了提供更多上下文,Config 和 Values 定义如下:

type BaseProps<T> = {
  initialValue: T;
  hidden?: boolean;
};

export interface TextInput extends BaseProps<string>, TextInputProps {
  type: 'text';
}

export interface Checkbox extends BaseProps<boolean> {
  type: 'checkbox';
}

type Config<T> = { [K in keyof T]: TextInput | Checkbox };
export type Values<T extends Fields> = {
  [K in keyof T]: Config<T>[K]['initialValue'];
};

此处,Config 表示映射类型,其中 T 中的每个键都映射到 TextInput 或 Checkbox。另一方面,Values 表示映射类型,其中 T 中的每个键都映射到 Config 中相应字段的初始值。

总而言之,函数 f 需要 Props 类型的 props,其中包含有关表单字段(fields)的信息和一个可选的提交函数(onSubmit)。 fields 属性是使用映射类型 (Config) 定义的,这些字段的初始值是使用另一个映射类型 (Values) 提取的。

这里的核心问题是是否有一种方法可以让 Values 自动推断出正确的类型。目前,values.age 类型被推断为

string | boolean
。这种歧义源于 Config 中使用“或”运算符,它允许使用 TextInput 或 Checkbox。

人们关注的不仅仅是技术方面,还包括设计含义。是否存在结构或架构调整可以导致更精确的类型推断?或者这种模糊性是设计中固有的,因此可以接受吗?

本质上,我们正在探索是否有一种方法可以细化类型推断机制,以准确确定values.age的类型。

f({
  fields: {
    name: {
      type: 'text',
      initialValue: 'John Doe',
    },
    age: {
      initialValue: true,
      type: 'checkbox',
    },
  },
  onSubmit: (values) => {
    console.log(values.age);
  },
});

我会在这里提供全部内容,方便复制。


type BaseProps<T> = {
  initialValue: T;
  hidden?: boolean;
};

export interface TextInput extends BaseProps<string> {
  type: 'text';
}

export interface Checkbox extends BaseProps<boolean> {
  type: 'checkbox';
}

type Config<T> = { [K in keyof T]: TextInput | Checkbox };
export type Fields = { [key: string]: unknown };
export type Values<T extends Fields> = {
  [K in keyof T]: Config<T>[K]['initialValue'];
};

export interface Props<T extends Fields = Fields> {
  fields: Config<T>;
  onSubmit?: (values: Values<T>) => void;
}

function f<T extends Fields = Fields>(props: Props<T>) {
  return null;
}

f({
  fields: {
    name: {
      type: 'text',
      initialValue: 'John Doe',
    },
    age: {
      initialValue: true,
      type: 'checkbox',
    },
  },
  onSubmit: (values) => {
    console.log(values.age);
  },
});

我尝试了多种方法来推断这一点,但不幸的是,没有一个被证明是成功的。

typescript generics type-inference
1个回答
0
投票

我发现从头开始更容易:

type Controls = {
    checkbox: boolean,
    text: string,
}

type Field = {[C in keyof Controls]: {
    type: C,
    initialValue: Controls[C],
}}[keyof Controls];

function f<F extends {[K in string]: Field}>(props: {
    fields: F;
    onSubmit: (values: {[K in keyof F]: Controls[F[K]['type']]}) => void;
}) {

}

如果你这样称呼它:

f({
  fields: {
    name: {
      type: 'text',
      initialValue: 'John Doe',
    },
    age: {
      type: 'checkbox',
      initialValue: true,
    },
  },
  onSubmit: (values) => {

  },
});

编译器检查:

  1. type
    是已知的控制类型,
  2. 该控件类型接受指定的
    initialValue

并推断

values
是类型

(parameter) values: {
    name: string;
    age: boolean;
}

特别注意,值的推断仅查看控件的类型,而不是

initialValue
的类型:我们不希望
age
仅仅因为
true
就推断为
initialValue
已知为
true

© www.soinside.com 2019 - 2024. All rights reserved.