Extending union type alias in typescript?

前端 未结 3 1948
隐瞒了意图╮
隐瞒了意图╮ 2020-12-16 15:54

I\'m trying to limit some string fields to be only of certain values at compile time. The problem is that those values should be extendable. Here\'s a simplified example:

3条回答
  •  暗喜
    暗喜 (楼主)
    2020-12-16 16:08

    I think you're using the word "extendable" in a different sense from what the keyword extends means. By saying the type is "extendable" you're saying you'd like to be able to widen the type to accept more values. But when something extends a type, it means that you are narrowing the type to accept fewer values.

    SomeInterface can essentially be only one of the following four types:

    • SomeInterface<'FOO'|'BAR'>: amember can be either 'FOO' or 'BAR'
    • SomeInterface<'FOO'>: amember can be only 'FOO'
    • SomeInterface<'BAR'>: amember can be only 'BAR'
    • SomeInterface: amember cannot take any value

    I kind of doubt that's actually what you want, but only you know that for sure.


    On the other hand, if you want SomeInterface to be defined such that T can always be either FOO or BAR, but also possibly some other string values, you want something that TypeScript doesn't exactly provide, which would be specifying a lower bound for T. Something like SomeInterfacesuperFoobar extends string>, which isn't valid TypeScript.

    But you probably only care about the type of amember, and not T. If you want amember to be either FOO or BAR, but also possibly some other string values, you could specify it like this:

    interface SomeInterface {
      amember: Foobar | T;
      [key: string]: string; 
    }
    

    where T is just the union of extra literals you'd like to allow. If you don't want to allow any extra, use never, or just leave out the type parameter (since I've put never as the default).

    Let's see it in action:

    const yes = {
        amember: 'FOO'
    } as SomeInterface; // good, 'FOO' is a Foobar
    
    const no = {
        amember: 'BAZ'
    } as SomeInterface; // bad, 'BAZ' is not a Foobar
    
    abstract class SomeClass {
      private anotherMember: SomeInterface;
    }
    
    class FinalClass extends SomeClass<'BAZ'> { 
    } // fine, we've added 'BAZ'
    
    // let's make sure we did:
    type JustChecking = FinalClass['anotherMember']['amember']
    // JustChecking === 'BAZ' | 'FOO' | 'BAR'
    

    Did I answer your question? Hope that helps.

提交回复
热议问题