TS4.1 的更新。现在可以通过以下方式在类型级别表示字符串连接模板字符串类型,实施于微软/TypeScript#40336 https://github.com/microsoft/TypeScript/pull/40336。现在您可以获取一个对象并在类型系统中获取其虚线路径。
Imagine languageObject
这是:
const languageObject = {
viewName: {
componentName: {
title: 'translated title'
}
},
anotherName: "thisString",
somethingElse: {
foo: { bar: { baz: 123, qux: "456" } }
}
}
首先我们可以使用递归条件类型如实施于微软/TypeScript#40002 https://github.com/microsoft/TypeScript/pull/40002 and 可变元组类型如实施于微软/TypeScript#39094 https://github.com/microsoft/TypeScript/pull/39094将对象类型转换为与其对应的键元组的并集string
- 有价值的属性:
type PathsToStringProps<T> = T extends string ? [] : {
[K in Extract<keyof T, string>]: [K, ...PathsToStringProps<T[K]>]
}[Extract<keyof T, string>];
然后我们可以使用模板字符串类型将字符串文字元组连接到点分路径(或任何分隔符D
:)
type Join<T extends string[], D extends string> =
T extends [] ? never :
T extends [infer F] ? F :
T extends [infer F, ...infer R] ?
F extends string ?
`${F}${D}${Join<Extract<R, string[]>, D>}` : never : string;
结合这些,我们得到:
type DottedLanguageObjectStringPaths = Join<PathsToStringProps<typeof languageObject>, ".">
/* type DottedLanguageObjectStringPaths = "anotherName" | "viewName.componentName.title" |
"somethingElse.foo.bar.qux" */
然后可以在签名中使用translate()
:
declare function translate(dottedString: DottedLanguageObjectStringPaths): string;
我们得到了我三年前谈论的神奇行为:
translate('viewName.componentName.title'); // okay
translate('view.componentName.title'); // error
translate('viewName.component.title'); // error
translate('viewName.componentName'); // error
Amazing!
Playground 代码链接 https://www.typescriptlang.org/play?ts=4.1.2#code/MYewdgzgLgBANgQzAcwK4OQUwPICMBWmwsAvDAN4CwAUDHTAG4CWmA7gHIIC2mAXBTXpCYoLgAdwmMFE49+VWsKVQmUOHxgByKACckERFEwATGCrWZNgpTAC+1urYA0DmEhBQAFph2yNAIi8mCABlXSYUfxdFOggQHiCUAFE4CA0FGxgAMxAQeRhcBB18woAvfgBGACYAZicYAEdUAA9+fwAWAFYANn87O1d7aiGaKABPMUwYAAUELwgAFRAwnQjkaZ0QMQgAHgWAPhgyBZhMZqMwYwgYaFWUGAB+GABtAF0YeVdngGkYCJgkuc9MQdgBrTBjEBZGALeq3Nb7V78H71AB06Nm8yWKzWGy2uwWP1eiJotmegN0CBB4Mh0NhN3CKERAG4aKMJlMAFIgCJ7U7nKRXBl3ZBveoAEX5FyF8KZR1cJzO0uub0eMDAmAYPg+CqlgpVESy2oAYu8nsadTEYXrLgawEadDBjWj0YbtQAlM2uC1K-XCtaPVwAAwAJORjbYw+LI+RubyKcCoDt3XDGaLiRL9rYgx91ZrtfxZchWdR2ZMYOKPEZjAAZJBoDA4AhEKA4lCYzzXMhxsA7DuLZZpvHbHbjSZQ+D19BYPCEYj7er+VH+fY0AD0ACozByK1WTHWUNOm3PW0O5p2jjB-O4vD4-H0AD5X5hsPyo0QSDXSN-mdSPmCuHQ-hxAknhrCkaSojkICooUOiok0zR9Bua5stQxhEIgOhTFkqBgMQTDgGYeiQIYmAABTGHuxhtsg-CVlA1YHg2M7NsQtH9gAlIWaYlqMJEGHMFGaC+HDcJg77xJ+UgyOJqK-pYnHMjAa5rjAICgggYz8foZHkSJLCsJJ4iSN+ckKZoSkqWpPibDoOmkUJ+miW+H6mVA8mqOolnKappw6HZDmCUYzmGa5UnuX4PnWf5gWltQQA
TS4.1 之前的答案:
如果你想让 TypeScript 帮助你,你就必须帮助 TypeScript。它不知道有关连接字符串文字的类型的任何信息,因此这是行不通的。我对如何帮助 TypeScript 的建议可能比您想要的更多,但它确实带来了一些相当不错的类型安全保证:
首先,我假设你有一个languageObject
and a translate()
知道它的函数(意味着languageObject
大概是用来生产特定的translate()
功能)。这translate()
函数需要一个点分字符串,表示嵌套属性的键列表,其中最后一个此类属性是string
-valued.
const languageObject = {
viewName: {
componentName: {
title: 'translated title'
}
}
}
// knows about languageObject somehow
declare function translate(dottedString: string): string;
translate('viewName.componentName.title'); // good
translate('view.componentName.title'); // bad first component
translate('viewName.component.title'); // bad second component
translate('viewName.componentName'); // bad, not a string
介绍Translator<T>
班级。您可以通过给它一个对象和一个来创建它translate()
该对象的函数,然后您调用它get()
链中的方法来深入了解密钥。当前值T
始终指向您通过链选择的财产类型get()
方法。最后,你打电话translate()
当你到达string
您关心的价值。
class Translator<T> {
constructor(public object: T, public translator: (dottedString: string)=>string, public dottedString: string="") {}
get<K extends keyof T>(k: K): Translator<T[K]> {
const prefix = this.dottedString ? this.dottedString+"." : ""
return new Translator(this.object[k], this.translator, prefix+k);
}
// can only call translate() if T is a string
translate(this: Translator<string>): string {
if (typeof this.object !== 'string') {
throw new Error("You are translating something that isn't a string, silly");
}
// now we know that T is string
console.log("Calling translator on \"" + this.dottedString + "\"");
return this.translator(this.dottedString);
}
}
初始化它languageObject
和translate()
功能:
const translator = new Translator(languageObject, translate);
并使用它。这按预期工作:
const translatedTitle = translator.get("viewName").get("componentName").get("title").translate();
// logs: calling translate() on "viewName.componentName.title"
这些都会根据需要产生编译器错误:
const badFirstComponent = translator.get("view").get("componentName").get("title").translate();
const badSecondComponent = translator.get("viewName").get("component").get("title").translate();
const notAString = translator.get("viewName").translate();
希望有帮助。祝你好运!