Typescript-基于其他类型的键约束泛型类型

Typescript-基于其他类型的键约束泛型类型,typescript,Typescript,我正在尝试编写一个typescript帮助程序库来强键入process.env,以便您可以执行以下操作。如果变量丢失或无法转换为正确的类型,则使用throw的库 从'@lib/env'导入{getEnv,num,str} const env=getEnv({ 表名称:num(), }) //环境的类型是 //常量环境:只读 我目前有下面的代码,它可以工作 类型验证程序=( 默认值?:T, )=>(processEnvKey:string,processEnvValue?:string)=>T

我正在尝试编写一个typescript帮助程序库来强键入
process.env
,以便您可以执行以下操作。如果变量丢失或无法转换为正确的类型,则使用throw的库

从'@lib/env'导入{getEnv,num,str}
const env=getEnv({
表名称:num(),
})
//环境的类型是
//常量环境:只读
我目前有下面的代码,它可以工作

类型验证程序=(
默认值?:T,
)=>(processEnvKey:string,processEnvValue?:string)=>T
类型验证器={[K in keyof T]:ReturnType}
导出常量str:Validator=(默认值)=>(
processEnvKey,
过程值,
) => {
if(processEnvValue!=null)返回processEnvValue
如果(defaultValue!=null)返回defaultValue
抛出新错误(
`环境变量“${processEnvKey}”是必需的,未提供默认值`,
)
}
导出常量num:Validator=(默认值)=>(
processEnvKey,
过程值,
) => {
if(processEnvValue!=null){
常量processEnvValueAsNumber=编号(processEnvValue)
if(Number.isNaN(processEnvValueAsNumber)){
抛出新错误(
`环境变量“${processEnvKey}”必须是数字,但无法将“${processEnvValue}”解析为数字`,
)
}
返回processEnvValueAsNumber
}
如果(defaultValue!=null)返回defaultValue
抛出新错误(
`环境变量“${processEnvKey}”是必需的,未提供默认值`,
)
}
导出常量getEnv=(
验证器:验证器,
环境=process.env,
):Readonly=>{
常量结果:部分={}
for(验证程序中的const processEnvKey){
常量验证器=验证器[processEnvKey]
结果[processEnvKey]=验证器(processEnvKey,环境[processEnvKey])
}
返回结果为只读
}
我现在有了一个新的需求,在这里我提前知道了作为接口可用的所有环境键

接口环境{
API_端点:任何
表名称:任何
}
因此,我正在尝试更改
getEnv
,以便传入的对象只能包含在
Env
中找到的键

我试图更改
getEnv
,但遇到了问题

export const getEnv=(
如果我把它改成

export const getEnv=(
我可以在编译器没有抱怨的情况下传递额外的键。即使
不存在的\u键
不是
Env的键,以下情况也不会导致编译器错误

const env=getEnv({
表名称:num(),
不存在的密钥:str(),
})
好主意

这种行为是由于设计的方式。 约束
T扩展{[K in keyof Env]?:unknown}
强制执行
T
的最低要求。当然,允许任何附加属性

您需要的是一个适当的属性,它可以防止定义任何多余的属性。 用户jcalz有一个可浏览以下内容之一的:

类型精确=T&Record;
这需要一个类型
T
和一个候选类型
U
,我们要确保它“精确地
T
”。它返回一个新类型,类似于
T
,但具有与
U
中的额外属性相对应的额外
never
值属性。如果我们将此用作
U
的约束,例如
U
精确地扩展
,那么我们可以保证
U
匹配
T
,并且没有额外属性皮质类固醇


使用此类型,您可以按如下方式声明
getEnv

export const getEnv = <T extends Exactly<{ [K in keyof Env]?: unknown }, T>>(
  validators: Validators<T>,
  environment = process.env,
): Readonly<T> => {};

如果您在第17/18行切换注释并刷新,Stackblitz将演示这一点。

尝试从
getEnv
中删除泛型
T
,并在函数的其他位置将
T
替换为
Env
的子类型。如果您想允许
T
成为
Env
的子类型,请在您的问题中替换
??
T[K]
。如果您不关心值的类型,请将
??
替换为
any
。因此
t
将仅限于特定键。请不要使用
any
,请使用
unknown
。对于此用例,您可以使用经过战斗测试的yup库,该库允许您定义验证模式并可以推断类型脚本或者免费使用
type Config=yup.InferType
// from
type Validators<T> = { [K in keyof T]: ReturnType<Validator<T[K]>> }
// to
type Validators<T> = { [K in keyof T]: T[K] extends never ? never : ReturnType<Validator<T[K]>> };