Typescript: 使用联合类型映射对象值未按预期工作

4
如果我想映射对象值并且所有值都是基本类型,那么很简单:
type ObjectOf<T> = { [k: string]: T };

type MapObj<Obj extends ObjectOf<any>> = {
  [K in keyof Obj]: Obj[K] extends string ? Obj[K] : 'not string';
};

type Foo = MapObj<{
  a: 'string',
  b: 123,
}>; // Foo is { a: 'string', b: 'not string' }

但是,当我将联合类型作为对象值时,TS的工作效果不如预期:

type AllPaths = '/user' | '/post';

type Props<Path extends AllPaths> = MapObj<{
  path: Path,
}>;

function Fn<Path extends AllPaths>({ path }: Props<Path>) {
  const path2: AllPaths = path;
}

我遇到了以下错误:

Type 'Path extends string ? Path : "not string"' is not assignable to type 'AllPaths'.
  Type 'Path | "not string"' is not assignable to type 'AllPaths'.
    Type '"not string"' is not assignable to type 'AllPaths'.
      Type 'Path extends string ? Path : "not string"' is not assignable to type '"/post"'.
        Type 'Path | "not string"' is not assignable to type '"/post"'.
          Type 'string & Path' is not assignable to type '"/post"'.

因为联合类型的成员都是字符串,所以我希望MapObj的输出仍然是字符串的联合类型。我该如何解决这个问题?

TS Playground: https://www.typescriptlang.org/play?#code/C4TwDgpgBA8gRgKwgY2DAZgHgCoD4oC8UA3lANoDWAXFAM7ABOAlgHYDmAujdlAL4DcAKEGhIUALIBDMPASZZUCAA9gEFgBNasRCjRZJLELnxFigqOQDSUVlAoQQAe3TaEXV2UsdFKtZrqMrGxQAPweXlA0AOQsjsABzOxRQgLCotAAggA2WQAKksAAFlpEUQD0AK60EAxRUAA+UOVgjvTJaeDQuQyOYLSY+UU+qhpa2XkFxSYS0rKYZhZgkzSDhQA0gry4QoLoFSyoTI4sUABiLAOTw35jOau0uAAUpEtDvCs9fZdFuACUJOYoMhjvQoK9CgAmGjje6EMGTFKCIA


https://www.typescriptlang.org/play?#code/C4TwDgpgBA8gRgKwgY2DAZgHgCoD4oC8UA3lANoDWAXFAM7ABOAlgHYDmAujdlAL4DcAKEGhIUALIBDMPASZZUCAA9gEFgBNasRCjRZJLELnxFigqOQDSUVlAoQQAe3TaEXV2UsdFKtZrqMrGxQAPweXlA0AOQsjsABzOxRQgLCotAAggA2WQAKksAAFlpEUQD0AK60EAxRUAA+UOVgjvTJaeDQuQyOYCUS0rKYZhZgBYU02XnjtAA0gry4QoLIjiz0UGA03b39pGNFNABEldUMR3zC6BUsqExrUABiLJj5RT6qGlpTb8W4ABT7cZ8bY9PoAShI5igq3W8QOhQATJMcr9+giFoIgA - Łukasz Karczewski
@ŁukaszKarczewski 我需要推断 Path,因为其他对象值需要使用它。 - Leo Jiang
1个回答

0

我认为问题在于,即使您像这样声明type Props<Path extends AllPaths> = MapObj<{ path: Path, }>;,它只会包含值'/user' | '/post',但是 TypeScript 静态类型检查可能无法识别此知识,因为它的类型是MapObj<{ path: Path, }>,这只是一个 js 对象的 MapObj。

所以只需使用as Path来妥协即可。

function Fn<Path extends AllPaths>({ path }: Props<Path>) {
  const path2: AllPaths = path as Path;
}

或者像这样为PathObj声明一个类型:

type AllPaths = '/user' | '/post';
type AllPathObj = {
  path: AllPaths,
}

type Props<PathObj extends AllPathObj> = MapObj<AllPathObj>;

function Fn<PathObj extends AllPathObj>({ path }: Props<PathObj>) {
  const path2: AllPaths = path;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接