键入对象中的键,其中该键的值满足约束

Type for keys in object, where value of that key satisfies constraint

我有这样的东西:

显然这是一个过于简单的例子,所以我为什么要专门做这些类型的问题是无关紧要的。

type Bird = { kind: "bird" }
type Fish = { kind: "fish" }

const zoo = {
  flamingo: { kind: "bird" },
  hawk: { kind: "bird" },
  chicken: { kind: "bird" },
  guppy: { kind: "fish" },
  blowfish: { kind: "fish" }
}

我想获取可能是这样的字符串数组类型:

type FishInZoo = "guppy" | "blowfish"
type BirdInZoo = "flamingo" | "hawk" | "chicken"

所以我在想类似下面的内容,但我不知道语法是什么:

type FishInZoo = keyof typeof zoo where { kind: "fish" }

这可能吗?

我相信您正在寻找类似的东西:

type GetKeysOfType<
  Type extends Record<string, any>,
  Obj extends Record<string, any>
> = keyof {
    [Key in keyof Obj as Obj[Key] extends Type ? Key : never]: Obj[Key];
}

请注意,这将需要对对象 see docs 进行 as const 断言。这允许您在对象和类型中使用字符串文字,即

type Bird = { kind: "bird" }
type Fish = { kind: "fish" }

// as opposed to
type Kind = { kind: string };

使用它需要您指定:

const zoo = {
  flamingo: { kind: "bird" },
  hawk: { kind: "bird" },
  chicken: { kind: "bird" },
  guppy: { kind: "fish" },
  blowfish: { kind: "fish" }
} as const // this as const.

然后你使用这样的类型:

type BirdsInZoo = GetKeysOfType<Bird, typeof zoo>
type FishesInZoo = GetKeysOfType<Fish, typeof zoo>

您还可以强制所有使用 GetKeysOfType 的对象必须是 Readonly,即必须使用 as const 以防止人们忘记和接收不正确的值。但这取决于您的用例。

它使用了4.1中发布的相对较新的key remapping feature

Playground link

除了上面@zecuria 的出色回答之外,还有一些额外的注意事项,与我对这种类型的实际使用有关。

type GetKeysOfType<
  Type extends Record<string, any>,
  Obj extends Record<string, any>
> = keyof {
  [Key in keyof Obj as Obj[Key] extends Type ? Key : never]: Obj[Key];
};

const initialFilters = {
  sportId: { key: "sportId", kind: "type", types: [] as String[] },
  gameType: { key: "gameType", kind: "type", types: [] as String[] },
  contestTypeId: { key: "contestTypeId", kind: "type", types: [] as String[] },
  entryFees: {
    kind: "range",
    key: "entryFees",
    max: 10000,
    min: 0,
  },
} as const;

type TypeFilterKey = GetKeysOfType<TypeFilter, typeof initialFilters>;

重要的是 initialFilters 中对象值的 as String[]

没有它,types 将是 readonly [] 类型,而不是可变的 String[] 类型,类似于

filter.types.includes(type)

将失败

TS2345: Argument of type 'string' is not assignable to parameter of type 'never'.