在 TypeScript 中,我可以指定对象字段的类型,同时仍能推断出文字键类型吗?
In TypeScript, can I specify the type of object fields while still getting the literal key types inferred?
我想做的是定义某种“丰富的枚举”,其中每个枚举键都链接到一些我想指定其类型的数据。
例如,像这样:
const Seasons = {
winter: { temperature: 5, startMonth: "December" },
spring: { temperature: 20, startMonth: "March" },
summer: { temperature: 30, startMonth: "June" },
fall: { temperature: 15, startMonth: "September" },
} as const
这个声明很好,让我可以做这样的事情:
type Season = keyof typeof Seasons // "winter" | "spring" | "summer" | "fall"
甚至像
这样的类型保护
function isSeason(s: string): s is Season {
return Object.keys(Seasons).includes(s)
}
不过,我不能做的是让编译器检查所有“季节定义”是否具有给定类型。如果我这样定义:
type SeasonData = typeof Seasons[Season]
那么SeasonData
是所有定义类型的并集——不管它们是否具有相同的形状。
所以我正在寻找一种语法上非冗余且轻便的方式来定义如下内容:
const Seasons: EnumWith<{temperature: number, startMonth: string}> = ... // as before
^^^^^^^^ <- to be defined!
特别是,我试图不必在任何其他结构(接口或数组)中重复季节列表,而是直接从对象定义中推断出类型 Season
(尽管听说替代方案是总是好的!)。
可以做什么?
我不确定我是否完全理解您的用例,我对您的类型之间的关系建模的方式类似于以下内容
type Season =
| "winter"
| "spring"
| "summer"
| "fall"
type Month =
| "January"
| "February"
| "March"
| "April"
| "May"
| "June"
| "July"
| "August"
| "September"
| "October"
| "November"
| "December"
type SeasonStruct = {
temperature: number
startMonth: Month
}
type Seasons = { [K in Season]: SeasonStruct }
const seasons: Seasons = {
winter: { temperature: 5, startMonth: "December" },
spring: { temperature: 20, startMonth: "March" },
summer: { temperature: 30, startMonth: "June" },
fall: { temperature: 15, startMonth: "September" },
}
这应该为您提供了足够的构建块来表示您域中所需的一切,希望对您有所帮助。
刚刚找到了一种有点复杂的方法来从键中提取文字类型信息,同时仍然检查值:
function EnumWith<P>() {
return function <K extends keyof any, R extends Record<K, P>>(defs: R): R {
return defs
}
}
允许这样写:
const Seasons = EnumWith<{
temperature: number
startMonth: Month // defined as in bugs's answer
}>()({
winter: { temperature: 5, startMonth: "December" },
spring: { temperature: 20, startMonth: "March" },
summer: { temperature: 30, startMonth: "June" },
fall: { temperature: 15, startMonth: "September" },
})
type Season = keyof typeof Seasons
关键是发现K extends keyof any
可以让你在一个泛型签名中捕获key的类型,并将两个泛型类型分成两个函数调用,这样我们就可以指定一个,让另一个被推断(目前不可能在 TypeScript 中的单个函数调用中)。
所以,当然,}>()({
行有点碍眼……
我想做的是定义某种“丰富的枚举”,其中每个枚举键都链接到一些我想指定其类型的数据。
例如,像这样:
const Seasons = {
winter: { temperature: 5, startMonth: "December" },
spring: { temperature: 20, startMonth: "March" },
summer: { temperature: 30, startMonth: "June" },
fall: { temperature: 15, startMonth: "September" },
} as const
这个声明很好,让我可以做这样的事情:
type Season = keyof typeof Seasons // "winter" | "spring" | "summer" | "fall"
甚至像
这样的类型保护function isSeason(s: string): s is Season {
return Object.keys(Seasons).includes(s)
}
不过,我不能做的是让编译器检查所有“季节定义”是否具有给定类型。如果我这样定义:
type SeasonData = typeof Seasons[Season]
那么SeasonData
是所有定义类型的并集——不管它们是否具有相同的形状。
所以我正在寻找一种语法上非冗余且轻便的方式来定义如下内容:
const Seasons: EnumWith<{temperature: number, startMonth: string}> = ... // as before
^^^^^^^^ <- to be defined!
特别是,我试图不必在任何其他结构(接口或数组)中重复季节列表,而是直接从对象定义中推断出类型 Season
(尽管听说替代方案是总是好的!)。
可以做什么?
我不确定我是否完全理解您的用例,我对您的类型之间的关系建模的方式类似于以下内容
type Season =
| "winter"
| "spring"
| "summer"
| "fall"
type Month =
| "January"
| "February"
| "March"
| "April"
| "May"
| "June"
| "July"
| "August"
| "September"
| "October"
| "November"
| "December"
type SeasonStruct = {
temperature: number
startMonth: Month
}
type Seasons = { [K in Season]: SeasonStruct }
const seasons: Seasons = {
winter: { temperature: 5, startMonth: "December" },
spring: { temperature: 20, startMonth: "March" },
summer: { temperature: 30, startMonth: "June" },
fall: { temperature: 15, startMonth: "September" },
}
这应该为您提供了足够的构建块来表示您域中所需的一切,希望对您有所帮助。
刚刚找到了一种有点复杂的方法来从键中提取文字类型信息,同时仍然检查值:
function EnumWith<P>() {
return function <K extends keyof any, R extends Record<K, P>>(defs: R): R {
return defs
}
}
允许这样写:
const Seasons = EnumWith<{
temperature: number
startMonth: Month // defined as in bugs's answer
}>()({
winter: { temperature: 5, startMonth: "December" },
spring: { temperature: 20, startMonth: "March" },
summer: { temperature: 30, startMonth: "June" },
fall: { temperature: 15, startMonth: "September" },
})
type Season = keyof typeof Seasons
关键是发现K extends keyof any
可以让你在一个泛型签名中捕获key的类型,并将两个泛型类型分成两个函数调用,这样我们就可以指定一个,让另一个被推断(目前不可能在 TypeScript 中的单个函数调用中)。
所以,当然,}>()({
行有点碍眼……