首页 > 解决方案 > 如何通过拆分字符串来推断嵌套对象值中的字符串文字?

问题描述

我正在尝试根据您在对象中定位的路径来构建所需的对象参数

type Split<S extends string, D extends string> =
    string extends S ? string[] :
    S extends '' ? [] :
    S extends `${infer T}${D}${infer U}` ? [T, ...Split<U, D>] :
    [S];

type PropType<T, Path extends string> =
    string extends Path ? unknown :
    Path extends keyof T ? T[Path] :
    Path extends `${infer K}.${infer R}` ? K extends keyof T ? PropType<T[K], R> : unknown :
    unknown; 

type ParseMustaches_<T extends string> = 
  T extends `${infer U}{{${infer V}}}${infer W}` 
    ? Record<V, string> 
    : never

type ParseMustaches<T extends string[]> = ParseMustaches_<T[number]>;

type Prop<T, K extends keyof T> = T[K];

declare function translate<T extends { [L in K]: string }, K extends string>(obj: T, path: K, placeholders: ParseMustaches<Split<T[K], " ">>): void;

const obj = {
  "title": "Welcome to {{sitename}}, {{user}}",
  "button": {
    "text": "Click here to go to {{location}}",
    "num": 5
  }
} as const;

translate(obj, "title", { sitename: "", user: "" }) // works
translate(obj, "button.text", { }) // does not work

它似乎适用于顶级属性,但嵌套属性失败,我该如何解决这个问题? 链接到游乐场

标签: typescripttypescript-typings

解决方案


问题出在您所说的部分T extends { [L in K]: string },因为K这里通常不是键,而是路径。所以你需要一些额外的工作来让你的魔法发生。

type Split<S extends string, D extends string> =
  string extends S ? string[] :
  S extends '' ? [] :
  S extends `${infer T}${D}${infer U}` ? [T, ...Split<U, D>] :
  [S];

type PropType<T, Path extends string> =
  string extends Path ? unknown :
  Path extends keyof T ? T[Path] :
  Path extends `${infer K}.${infer R}` ? K extends keyof T ? PropType<T[K], R> : unknown :
  unknown;

type ParseMustaches_<T extends string> =
  T extends `${infer U}{{${infer V}}}${infer W}`
  ? Record<V, string>
  : never

type ParseMustaches<T extends string[]> = ParseMustaches_<T[number]>;

// You need to this utility type
type TypeOnPath<Path extends string, T> =
  string extends Path ? unknown :
  Path extends `${infer K}.${infer R}` ? { [k in K]: TypeOnPath<R, T> } :
  { [k in Path]: T };

declare function translate<K extends string, T extends TypeOnPath<K, string>>
  (obj: T, path: K, placeholders: ParseMustaches<Split<PropType<T, K> & string, " ">>): void;

const obj = {
  "title": "Welcome to {{sitename}}, {{user}}",
  "button": {
    "text": "Click here to go to {{location}}",
    "num": 5
  }
} as const;

translate(obj, "title", { sitename: "", user: "" })
translate(obj, "button.text", {}) // now we have an error saying that 'location' is missing
translate(obj, "button.num", {}) // correctly report error saying 'button.num' is not a string

看到这个游乐场链接

更新

要获得评论中提到的所需行为,您可以修改Split

// Remove constraint on S so that it takes any input type
type Split<S, D extends string> =
  S extends string ? (
    string extends S ? string[] :
    S extends '' ? [] :
    S extends `${infer T}${D}${infer U}` ? [T, ...Split<U, D>] :
    [S]
  ) : never;

type PropType<T, Path extends string> =
  string extends Path ? unknown :
  Path extends keyof T ? T[Path] :
  Path extends `${infer K}.${infer R}` ? K extends keyof T ? PropType<T[K], R> : unknown :
  unknown;

type ParseMustaches_<T extends string> =
  T extends `${infer U}{{${infer V}}}${infer W}`
  ? Record<V, string>
  : never

type ParseMustaches<T extends string[]> = ParseMustaches_<T[number]>;

type TypeOnPath<Path extends string, T> =
  string extends Path ? unknown :
  Path extends `${infer K}.${infer R}` ? { [k in K]: TypeOnPath<R, T> } :
  { [k in Path]: T };

// This time we use TypeOnPath<K, any>
declare function translate<K extends string, T extends TypeOnPath<K, any>>
  (obj: T, path: K, placeholders: ParseMustaches<Split<PropType<T, K>, " ">>): void;

const obj = {
  "title": "Welcome to {{sitename}}, {{user}}",
  "button": {
    "text": "Click here to go to {{location}}",
    "num": 5
  }
} as const;

translate(obj, "title", { sitename: "", user: "" })
translate(obj, "button.text", {}) // now we have an error saying that 'location' is missing
translate(obj, "button.num", {}) // error with never

看到这个游乐场链接


推荐阅读