可分配但可以用不同的子类型实例化

Is assignable but could be instantiated with a different subtype

我想创建一个通用函数来处理仅传递的参数类型,而不是枚举中的任何类型。

有一个简化的例子。

enum EAnimals {
  dog = 'dog',
  cat = 'cat',
}

interface IConfig<T extends EAnimals> {
  animalType: T
}

const configs: {[animalType in EAnimals]: IConfig<animalType>} = {
  [EAnimals.cat]: {
    animalType: EAnimals.cat
  },
  [EAnimals.dog]: {
    animalType: EAnimals.dog
  }
}

function doSomething<T extends EAnimals> (animalTypeParam: T): IConfig<T> {
  return configs[animalTypeParam]; // error
}

我看过 60730845 但我想使用泛型。例如,这里不可能为 EAnimals.cat 创建配置,其中 animalTypeEAnimals.cat 以外的任何东西。如果我这样做 doSomething(EAnimals.cat),我将知道返回的配置是针对 cat。如果我将参数的类型声明为 animalTypeParam: EAnimals,我还必须将返回的类型声明为 IConfig<EAnimals>,这会破坏这个想法。

我知道这是因为 T extends ... 可以比原始枚举大,但我找不到类似 in 的泛型。

这样做的主要目的是在创建配置和使用此配置的函数时消除人为因素。如果有其他可行的解决方案,我很想听听。

只是重载它:

enum EAnimals {
    dog = 'dog',
    cat = 'cat',
}

interface IConfig<T extends EAnimals> {
    animalType: T
}

const configs: { [animalType in EAnimals]: IConfig<animalType> } = {
    [EAnimals.cat]: {
        animalType: EAnimals.cat
    },
    [EAnimals.dog]: {
        animalType: EAnimals.dog
    }
}

function doSomething<T extends EAnimals>(animalTypeParam: T):IConfig<T>
function doSomething<T extends EAnimals>(animalTypeParam: T) {
    return configs[animalTypeParam]; // ok
}

const result = doSomething(EAnimals.cat) // IConfig<EAnimals.cat>

Playground

更新

有两种处理方法。

首先-进行部分应用

enum EAnimals {
    dog = 'dog',
    cat = 'cat',
}

interface IConfig<T extends EAnimals> {
    animalType: T
}

const configs = {
    [EAnimals.cat]: {
        animalType: EAnimals.cat
    },
    [EAnimals.dog]: {
        animalType: EAnimals.dog
    }
} as const

type Config = { [animalType in EAnimals]: IConfig<animalType> }

const doSomething = <C extends Config>(config: C) => <T extends EAnimals>(animalTypeParam: T) => {
    return config[animalTypeParam]; 
}

const result = doSomething(configs)(EAnimals.cat)

但是,您需要将 configs 设为不可变对象。

其次 - 不要创建配置变量

enum EAnimals {
    dog = 'dog',
    cat = 'cat',
}

interface IConfig<T extends EAnimals> {
    animalType: T
}


type Config = { [animalType in EAnimals]: IConfig<animalType> }

const doSomething = <C extends Config>(config: C) => <T extends EAnimals>(animalTypeParam: T) => {
    return config[animalTypeParam];
}

const result = doSomething({
    [EAnimals.cat]: {
        animalType: EAnimals.cat
    },
    [EAnimals.dog]: {
        animalType: EAnimals.dog
    }
})(EAnimals.cat)