Typescript 使用具有强类型字段的对象设置泛型可选映射

Typescript 使用具有强类型字段的对象设置泛型可选映射,typescript,typescript-generics,Typescript,Typescript Generics,我正在尝试编写代码来注册强类型数据加载程序,但是我在使用typescript来正确设置映射时遇到了问题。在下面的示例中,M是服务地图,k是字段类型为E的服务列表。但是,当我尝试将一个实例分配给映射时,会出现一个错误,表示我正在尝试将其分配给undefined。不知道从这里到哪里去 enum E { A = 'a', B = 'b', } interface I<T extends E> { type: T; } type J<T> = T extends

我正在尝试编写代码来注册强类型数据加载程序,但是我在使用typescript来正确设置映射时遇到了问题。在下面的示例中,
M
是服务地图,
k
是字段类型为
E
的服务列表。但是,当我尝试将一个实例分配给映射时,会出现一个错误,表示我正在尝试将其分配给undefined。不知道从这里到哪里去

enum E {
  A = 'a',
  B = 'b',
}

interface I<T extends E> {
  type: T;
}

type J<T> = T extends E ? I<T> : never;
export type K = J<E>;

const M: { [T in E]?: I<T> } = {};
const k: K[] = [];

k.forEach(
  <T extends E>(i: I<T>) => {
    M[i.type] = i;
    // ERROR
    // Type 'I<T>' is not assignable to type '{ a?: I<E.A> | undefined; b?: I<E.B> | undefined; }[T]'.
    //  Type 'I<T>' is not assignable to type 'undefined'.
  });
enum E{
A=‘A’,
B=‘B’,
}
接口I{
类型:T;
}
类型J=T扩展到E?I:从来没有;
出口类型K=J;
常数M:{[T in E]?:I}={};
常数k:k[]=[];
k、 弗雷奇(
(i:i)=>{
M[i.type]=i;
//错误
//类型“I”不可分配给类型“{a?:I |未定义;b?:I |未定义;}[T]”。
//类型“I”不可分配给类型“undefined”。
});

这是TypeScript编译器当前的一个限制,或者可能是多个此类限制的组合。我在这里看到的相关问题是:

  • (支持
    扩展\u oneof
    泛型约束)和(支持将泛型索引转换为非泛型映射类型,正是上面的用例):

    目前,无法说约束到联合的类型参数(如上面的
    T extends E
    )一次只能接受联合的单个元素。现在,
    T扩展E
    意味着
    T
    可以用以下四种类型中的任何一种指定:
    never
    E.A
    E.B
    、或
    E.A | E.B
    (即
    E
    )。如果可以告诉编译器,
    T
    可能是
    E.A
    ,或者可能是
    E.B
    ,但不能是它们的并集,那么编译器可能会意识到您的赋值对其中每一个都有效,并同时接受它们

    但事实并非如此
    T
    可以用
    E.A | E.B
    指定,因此
    M[i.type]=i
    必须适用于这种情况。因此,您的泛型回调函数也可以更改为其非泛型版本

    k.forEach((i: I<E>) => { M[i.type] = i; }); // error!
    // --------------------> ~~~~~~~~~
    // Type 'I<E>' is not assignable to type 'undefined'
    
    您可以使用一些稍微不那么不安全的断言:

    k.forEach(
      <T extends E>(i: I<T>) => {
        M[i.type] = i as any as (typeof M)[T];
      });
    
    这显然是多余的,而不是你想做的。但它确实表明编译器原则上可以理解
    M[i.type]=i
    是安全的,前提是可以告诉编译器假装执行了这样的案例枚举(如microsoft/TypeScript#25051)


    看起来像是另一位候选人;编译器无法验证这是否安全。根据我的经验,您可能只想使用一个类型断言,如,然后继续
    k.forEach(
      <T extends E>(i: I<T>) => {
        M[i.type] = i as any as (typeof M)[T];
      });
    
    k.forEach(
      <T extends E>(i: I<T>) => {
        (M as { [K in T]?: I<K> })[i.type] = i;
      });
    
    k.forEach(i => i.type === E.A ?
      M[i.type] = i :
      M[i.type] = i
    )