如何从TypeScript中的对象数组中提取字符串类型的并集?

时间:2018-10-23 21:24:38

标签: typescript

我正在React中构建一个Dropdown组件。它包含一个options对象和一个可选的value对象,它将预先选择给定的options中的一个。我希望能够键入value,以便它必须是options中给定的名称之一。

单个option如下所示:

interface Option {
    name: string;
    value: string;
}

该组件的props如下所示:

interface Props {
    options: Option[];
    value?: ValidOption; // How can I write ValidOption?
}

是否可以使value必须是options数组中的值之一?

例如,给定以下选项:

const options = [
    { name: 'Option1', value: 'option1' },
    { name: 'Option2', value: 'option2' },
]

我希望ValidOption等于:

type ValidOption = 'option1' | 'option2';

1 个答案:

答案 0 :(得分:0)

您的问题类似于this question。您无法定义单一类型Props来接受{options, value}形式的对象,其中valueoptions之一。您可以通过为有效选项引入类型参数O来开始:

interface Option<O extends string> { 
    name: string;
    value: O;
}
interface Props<O extends string> {
    options: Option<O>[];
    value?: O;
}

但是您遇到两个问题:

  1. 您无法编写表示“ Props<O>代表某些O”的单一类型。这将是existential type,TypeScript当前不支持。 Props<any>不起作用:它对选项和值没有任何限制。
  2. 如果呼叫者手动指定O,则无法强制options包含 all O的值。

鉴于这些问题,最接近的方法是编写一个通用函数,该函数可以使用{options, value}对象文字进行调用,这样,如果值是选项之一,则类型推断将成功,并且失败如果不是这样。根据您其余代码的结构,此方法可能有用也可能无效。

// Building on `Option` and `Props` defined above.

// Needed to prevent widening when storing the options array in a local
// variable.  If the options array is passed directly to `checkProps`,
// this isn't needed.
function asOptions<O extends string>(options: Option<O>[]) { 
    return options;
}

const options = asOptions([
    { name: 'Option1', value: 'option1' },
    { name: 'Option2', value: 'option2' },
]);

// We need to use two type parameters to detect errors.  If we use just
// `O`, then TypeScript will try to be helpful and infer `O` to be the
// union of the options and the value. 
function checkProps<O extends string, O1 extends O>(
    props: { options: Option<O>[], value?: O1 }) { 
    return props;
}

let myProps1 = checkProps({
    options: options,
    value: "option1"  // OK
});

let myProps2 = checkProps({
    options: options,
    value: "option3"  // Error
});

let myProps3 = checkProps({
    options: [
        { name: 'Option1', value: 'option1' },
        { name: 'Option2', value: 'option2' },
    ],
    value: "option3"  // Error
});