1

我有一个这样的对象(在 ts playground 中演示):

const sites = {
    stack: {url: 'https://stackoverflow.com/'},
    google: {url: 'https://www.google.com/'},
    azure: {url: 'https://portal.azure.com/'}
} as const

我想做的是创建一个包含所有使用过的键的联合类型,我可以这样做:

type SiteNames = keyof typeof sites; // "stack" | "google" | "azure"

但是,我还想将类型安全添加到sites初始化中,其中所有对象值都是这样的某种类型(在 ts playground 中演示):

interface ISiteDetails {
    url: string;
}

const sites: Record<string, ISiteDetails> = {
    stackoverflow: {url: 'https://stackoverflow.com/'},
    google: {url: 'https://www.google.com/'},
    azure: {url: 'https://portal.azure.com/'}
} as const

这在创建时提供了一些类型检查sites,但也从最终类型中删除了const 断言,所以现在 SiteNames 只是解析为一个字符串:

type SiteNames = keyof typeof sites; // string

问题:有没有办法两者兼得?创建时强类型Record<any, ISiteDetails化还能将所有对象键提取到新的联合类型中吗?

解决方法:不符合人体工程学,但我可以通过将站点重新分配给这样的导出变量来添加最后一层类型检查(在 ts playground 中演示):

const SitesTyped: Record<SiteNames, ISiteDetails> = sites;
4

1 回答 1

1

这通常通过身份功能完成。这将允许您在仍然使用它们的特定类型的同时限制输入(在 ts playground 中演示):

function defineSites<T extends Record<string, ISiteDetails>>(template: T) {
    return template;
}

const sites = defineSites({
    stackoverflow: {url: 'https://stackoverflow.com/'},
    google: {url: 'https://www.google.com/'},
    azure: {url: 'https://portal.azure.com/'}
})

我有一个小单线,有时我会进口。这是更高阶的身份

export const HOI = <Constraint> () => <T extends Constraint> (definition: T) => definition;

export const defineSites = HOI<Record<string, ISiteDetails>>();
// use as normal

如果您在.tsx文件中需要它,您可能希望将其编写为函数

export function HOI<Constraint>() { 
    return () => <T extends Constraint> (definition: T) => definition;
}
于 2021-11-10T01:02:23.613 回答