如何在TypeScript中最好地声明字符串结构?

3

我可以这样说,我有一个JavaScript对象:

const myObject = {
   id: 'my_id', // Base value that following value's structures will be depended on (always pascal case). 
   upperID: 'MY_ID', // Uppercase of `id`.
   camelID: 'myId', // Camel case of `id`.
}

我希望使用TypeScript确保id始终是帕斯卡小写格式。 upperIDcamelID与上述的不同字符串结构具有相同的“值”。在TypeScript中,最佳方式声明myObject类型是什么?


2
我认为这在TypeScript中是不可能的。我能想到的最好的方法是运行时检查。为什么这个问题被标记为模板字面量? - jabaa
1
@jabaa 因为我能找到的与这种方法最接近的东西就是模板文字文档,所以我猜想我找不到答案的原因可能是因为它根本就不可能实现..? :( - passionateLearner
1
@jabaa 有人使用 TypeScript 字符串类型实现了 SQL。这不是什么新鲜事物 https://github.com/codemix/ts-sql - Alex Wayne
2
这里,您可以找到用于创建驼峰/帕斯卡命名属性的类型实用程序。此外,在这里您还可以找到相关答案。 - captain-yossarian from Ukraine
1个回答

3
“提供的Uppercase<T>实用类型可以很容易地将文本转换为大写。”
type CasedIds<ID extends string> = {
    id: ID,
    upperID: Uppercase<ID>
}


const myObject: CasedIds<'my_id'> = {
   id: 'my_id',
   upperID: 'MY_ID',
} as const

查看游乐场


驼峰式大小写转换可能有些棘手。首先,您需要一个能够将字符串转换为驼峰式的类型。

可能有几种方法可以实现这一点。以下是其中一种方法。

type CamelCase<T extends string> =
    T extends `${infer Pre}_${infer Letter}${infer Post}`
        ? `${Pre}${Capitalize<Letter>}${CamelCase<Post>}`
        : T

type TestCamel = CamelCase<'abc_def_ghi'> // 'abcDefGhi'

查看游乐场

让我们来详细了解一下。

这个通用类型以字符串类型作为泛型参数T。然后检查T是否扩展了一个包含下划线后跟一些字符的特定模式的字符串。

如果是这样,那么从该模式推断出一些子字符串。将下划线前面的部分推断为Pre,将下划线后面的字符推断为Letter,将字符串的其余部分推断为Post。否则,就直接使用字符串类型。
然后我们可以从PreLetter大写以及Post中制作一个新字符串。但可能会有更多的下划线,因此我们在Post上再次执行整个过程。这是一种递归类型,在没有下划线剩余时停止递归。
使用它,其余的就很容易了:
type CamelCase<T extends string> =
    T extends `${infer Pre}_${infer Letter}${infer Post}`
        ? `${Pre}${Capitalize<Letter>}${CamelCase<Post>}`
        : T

type CasedIds<ID extends string> = {
    id: ID,
    upperID: Uppercase<ID>
    camelID: CamelCase<ID>
}


const myObject: CasedIds<'my_id'> = {
   id: 'my_id',
   upperID: 'MY_ID',
   camelID: 'myId',
} as const

查看游乐场


尽管你可能希望有一个函数来为你构建它们:
function makeId<T extends string>(id: T): CasedIds<T> {
    return {} as unknown as CasedIds<T> // mock a real implementation
}

const myId = makeId('my_id')
myId.id       // type: 'my_id'
myId.upperID  // type: 'MY_ID'
myId.camelID  // type: 'myId'

看操场


代码高尔夫技巧:使用 null! 而不是 {} as ... - zenly
此外,如果您遇到性能问题(如果由于某种原因您在此处使用了超长字符串),您可能需要考虑使用TCO类型。 - zenly
我实际上会使用 declare 并避免实现,但我发现这会让不熟悉它的人感到困惑。 - Alex Wayne

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