How to ensure TypeScript that string|string[] is string without using as?

前端 未结 2 2018
旧时难觅i
旧时难觅i 2021-01-20 04:32

edit
Due to course of time, this question has lost its validity, as it seems from the comments and answers to this one. Despite initial appearance, it\

相关标签:
2条回答
  • 2021-01-20 04:38

    Besides generics you can use function overloading

    function getI18n(id: string[]): string[];
    function getI18n(id: string): string;
    function getI18n(id: string | string[]): string | string[] {
        if (typeof id === 'string') {
            return id + '_title';
        }
        return id.slice();
    }
    
    const title = getI18n('test'); // const title: string
    const titles = getI18n(['a', 'b', 'c']); // const titles: string[]
    

    Link to official docs on this feature: functions overloads

    0 讨论(0)
  • 2021-01-20 05:02

    You can achieve the effect by generic type property. Consider:

    function getI18n<A extends string[] | string>(id: A): A { 
        return id; // example implementation
    }
    
    const arr = getI18n(['a', 'b']) // arr is string[]
    const str = getI18n('a') // str is string
    

    The downside of generic approach

    As the solution works great for id function, the issue starts with any implementation, TS is complaining about types as if the only way is to pass arguments without any modifications. Consider:

    function getI18n<A extends string[] | string>(id: A): A { 
        if (typeof id === 'string') {
            return id.concat('suffix') as A; 
        } else {
            return (id as string[]).map(x => x.concat('suffix')) as A;
        }
    }
    

    The solution works nice, but we need to consider some things:

    • type assertions need to be used in the body of the function
    • code not shows nicely what input type maps to what output (overloads show that)
    • type inference of the output will wrongly infer type of input

    The last point issue can be viewed in the example below:

    const str = getI18n('a') // str is type "a"
    

    So output type is "a" but in example implementation result will be a string "asuffix", so the type is wrong.

    Don't do that, function should have one input type

    I want to add one thing to the whole topic. The fact that we have the demand to have such polymorphic input is generally inherited with common JS approach, were such things are considered as historically good practice. But in reality function which has specific one input will be better, it creates less confusion and questions.

    Create function with monomorphic type.

    function getI18n(ids: string[]): string[] { 
        return ids.map(id => id + "suffix");
    }
    
    const arr = getI18n(['a', 'b'])
    const str = getI18n(['a'])
    

    Simple as that. Benefits of the approach:

    • no conditions on type level
    • no conditions on value level
    • no questions - what f will return for such and such input

    There is really no cost in putting a string into array brackets.

    0 讨论(0)
提交回复
热议问题