【问题标题】:Dynamically resolve property type based on another property value in TypeScript根据 TypeScript 中的另一个属性值动态解析属性类型
【发布时间】:2021-12-21 00:34:58
【问题描述】:

我有以下 TypeScript 界面:

interface SelectProps {
    options: Option[];
    value: string[];
    onChange: (value: string[]) => void;
}

我想添加名为 isMultiple 的布尔值,它将更改其他属性的类型。

isMultiple=true

  • 强制执行value:string[]
  • 强制执行onChange: (value: string[]) => void;

isMultiple=false

  • 强制执行value:string
  • 强制执行onChange: (value: string) => void;

是否可以根据一个属性的值动态设置其他属性的类型?

【问题讨论】:

    标签: typescript


    【解决方案1】:

    有点晚了,但我希望它可以帮助像它帮助我的其他人。

    可区分联合,也称为标记联合或代数数据类型可用于解决此问题。

    interface MultipleSelectProps {
        isMultiple: true;
        options: string[];
        value: string[];
        onChange: (value: string[]) => void;
    }
    
    interface SingleSelectProps {
        isMultiple: false;
        options: string[];
        value: string;
        onChange: (value: string) => void;
    }
    
    type SelectProps = MultipleSelectProps | SingleSelectProps;
    

    使用示例:

    function Select(props: SelectProps) {
        if (props.isMultiple) {
            const { value, onChange } = props;
            onChange(value);
        } else if (props.isMultiple === false) {
            const { value, onChange } = props;
            onChange(value);
        }
    }
    

    注意:当isMultipleundefinednull 时,无法推断出SelectProps 的具体类型。在这些情况下有必要做一个严格的比较isMultiple === false

    来源:https://blog.mariusschulz.com/2016/11/03/typescript-2-0-tagged-union-types

    【讨论】:

    • 这似乎适用于调用代码,但接受 SelectProps 的函数似乎无法区分 onChange 调用。有关示例,请参见 this code
    • 那是因为 is multiple 可以是未定义的或空的。在这些情况下,无法推断出SelectProps 的具体类型。完整答案here
    • 太棒了!如果您使用 function Select(props: SelectProps) 更新您的答案,那么我将标记为正确答案!
    • 有没有办法将 check onChange value 参数作为options 之一输入?所以 onChange 回调也可以根据正确的值进行验证?我猜options 在这种情况下不可能是动态的?
    • 我想我没有理解最后一个问题。你能举个例子吗?
    【解决方案2】:

    您可以使用打字稿分布式条件类型 https://www.typescriptlang.org/docs/handbook/2/conditional-types.html

    所以你的类型看起来像这样:

    type SelectProps<TMultiple = boolean> = TMultiple extends true
      ? {
          isMultiple: TMultiple;
          options: string[];
          value: string[];
          onChange: (value: string[]) => void;
        }
      : {
          isMultiple: TMultiple;
          options: string[];
          value: string;
          onChange: (value: string) => void;
        };
    

    这是一个简单的例子:https://stackblitz.com/edit/typescript-9jgvys

    【讨论】:

    • 这应该是公认的答案。
    【解决方案3】:

    不,你不能这样做,但这里有两种选择

    (1) 对valueonChange 使用联合类型:

    interface SelectProps {
        options: Option[];
        isMultiple: boolean;
        value: string | string[];
        onChange: (value: string | string[]) => void;
    }
    

    (2) 两种情况使用两种不同的接口:

    interface SelectProps {
        options: Option[];
        isMultiple: boolean;
    }
    
    interface SingleSelectProps extends SelectProps {
        value: string;
        onChange: (value: string) => void;
    }
    
    interface MultipleSelectProps extends SelectProps {
        value: string[];
        onChange: (value: string[]) => void;
    }
    
    function isSingle(props: SelectProps): props is SingleSelectProps {
        return !props.isMultiple;
    }
    
    function isMultiple(props: SelectProps): props is MultipleSelectProps {
        return props.isMultiple;
    }
    

    (code in playground)

    【讨论】:

    • 第一种方法的一个问题是value 可能是stringonChange 可能是(value: string[]) =&gt; void
    • @AntonyO'Neill 是的,你是对的。另一个(已接受)答案中的解决方案更好。
    猜你喜欢
    • 2022-11-24
    • 2022-12-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-21
    • 1970-01-01
    相关资源
    最近更新 更多