代码之家  ›  专栏  ›  技术社区  ›  durilka

在TypeScript中扩展联合类型别名?

  •  19
  • durilka  · 技术社区  · 7 年前

    我试图在编译时将一些字符串字段限制为只有某些值。问题是这些值应该是可扩展的。 下面是一个简化示例:

    type Foobar = 'FOO' | 'BAR';
    
    interface SomeInterface<T extends Foobar> {
      amember: T;
    
      [key: string]: string; // this really has to stay
    }
    
    // let's test it
    
    const yes = {
        amember: 'FOO'
    } as SomeInterface<'FOO'>; // compiles as expected
    
    //    const no = {
    //        amember: 'BAZ'
    //    } as SomeInterface<'BAZ'>; // Type '"BAZ"' does not satisfy the constraint 'Foobar' as expected
    
    // so far so good
    // Now the problem
    
    abstract class SomeClass<T extends Foobar> {
      private anotherMember: SomeInterface<T>;
    }
    
    type Foobarbaz = Foobar | 'BAZ';
    
    class FinalClass extends SomeClass<Foobarbaz> { //no good anymore
    }
    

    类型“Foobarbaz”不满足约束“Foobar”。类型 “BAZ”不可分配给类型“Foobar”。

    或者这是一个XY问题,有明显更好的解决方案吗?

    Typescript 2.3.4,但如果有魔力的话,我想我可以升级到2.4。

    3 回复  |  直到 3 年前
        1
  •  30
  •   jcalz    7 年前

    我认为你使用“可扩展”这个词的意义与关键字不同 extends 方法通过说类型是“可扩展的”,你是说你希望能够 要接受的类型 价值观但是当有什么 缩小 要接受的类型 较少的 价值观

    SomeInterface<T extends Foobar> 基本上只能是以下四种类型之一:

    • SomeInterface<'FOO'|'BAR'> amember 可以是 'FOO' 'BAR'
    • SomeInterface<'FOO'> : 只能是 “FOO”
    • SomeInterface<'BAR'> : 只能是 '条形图'
    • SomeInterface<never> A成员 无法获取任何值


    SomeInterface<T> 定义如下: T 总是 要么是 FOO BAR string 值,您需要TypeScript不能完全提供的内容,这将为 T . 类似于 SomeInterface<T super Foobar extends string> ,它不是有效的TypeScript。

    A成员 T . 如果你想的话 A成员 要么 酒吧 ,但也可能是其他的 值,可以这样指定:

    interface SomeInterface<T extends string = never> {
      amember: Foobar | T;
      [key: string]: string; 
    }
    

    哪里 只是您希望允许的额外文字的并集。如果您不想允许任何额外的,请使用 never 从不

    让我们看看它的实际效果:

    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<T extends string> {
      private anotherMember: SomeInterface<T>;
    }
    
    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'
    

    我回答你的问题了吗?希望这有帮助。

        2
  •  9
  •   Dom    4 年前

    为了实现您的需求,您可以使用 intersection 通过 &

    type Foobar = 'FOO' | 'BAR';
    type FoobarBaz = Foobar | & 'BAZ'; // or: 'BAZ' | & Foobar
    

    Example of intersected type

        3
  •  2
  •   artem    7 年前

    在typescript中,如何将“type”限制为仅包含某些字符串,但可以与其他字符串一起扩展?

    不知道您的X问题是什么,但您始终可以引入另一个泛型参数,并通过声明扩展该参数来限制类型。Typescript 2.3 supports default types for generic parameters Foobar 默认情况下,您可以使用 SomeInterface 与之前一样,使用一个参数,当您需要它扩展其他内容时,您可以明确提供:

    type Foobar = 'FOO' | 'BAR';
    
    interface SomeInterface<T extends X, X extends string=Foobar> {
      amember: T;
    
      [key: string]: string; // this really has to stay
    }
    
    // let's test it
    
    const yes = {
        amember: 'FOO'
    } as SomeInterface<'FOO'>; // compiles as expected
    
    
    abstract class SomeClass<T extends X, X extends string=Foobar> {
      private anotherMember: SomeInterface<T, X>;
    }
    
    type Foobarbaz = Foobar | 'BAZ';
    
    class FinalClass extends SomeClass<Foobarbaz, Foobarbaz> { 
    }
    

    我想我现在明白这个问题了。一种解决方案是将Foobar之类的联合类型编码为 keyof 一些人工接口类型仅用于表示键(值类型不使用,也无关紧要)。这样,通过扩展接口,您可以“自然地”扩展键集:

    interface FoobarKeys { FOO: { }; BAR: { } };
    
    type Foobar = keyof FoobarKeys;
    
    interface SomeInterface<X extends FoobarKeys = FoobarKeys> {
      amember: keyof X;
    
      [key: string]: string; // this really has to stay
    }
    
    
    
    abstract class SomeClass<X extends FoobarKeys = FoobarKeys> {
        protected anotherMember: SomeInterface<X> = {
            amember: 'FOO'
        };
    
        protected amethod(): void { 
            this.bmethod(this.anotherMember); // no error
        }
    
        protected bmethod(aparam: SomeInterface<X>): void { 
    
        }
    }
    
    // let's extend it
    
    interface FoobarbazKeys extends FoobarKeys { BAZ: {} };
    type Foobarbaz = keyof FoobarbazKeys;
    
    class FinalClass extends SomeClass<FoobarbazKeys> {
        private f() {
            this.bmethod({amember: 'BAZ'})
        } 
    }