使用递归条件类型时函数参数类型会折叠为any

问题描述 投票:0回答:1

这是 Github 上 Typescript 项目中发布的 issue 的后续内容。

Joe Calzaretta (@jcalz) 好心地为我的第一个递归条件类型问题提供了解决方案,但我现在面临另一个问题,函数参数变为

any
类型而不是预期类型。

以下是显示问题的代码摘录:

type ValidationContext<ValueType> = { value: ValueType }
type Constraint<ValueType, ConstraintType> =
    ConstraintType | ((context: ValidationContext<ValueType>) => ConstraintType)

type StringType = string | null | undefined
type StringConstraints = {
    required?: Constraint<StringType, boolean>,
    length?: Constraint<StringType, number>
}
type StringSchema = readonly ["string", StringConstraints]

type ObjectType = object | null | undefined
type ObjectConstraints = { required?: Constraint<ObjectType, boolean> } | undefined
type ObjectSchema<T extends ObjectType> =
    readonly ["object", ObjectConstraints, { [P in keyof T]: SchemaFor<T[P]> }]


type SchemaFor<T> =
    [T] extends [StringType] ? StringSchema :
    [T] extends [ObjectType] ? ObjectSchema<T> :
    never

type AnySchema = StringSchema | ObjectSchema<ObjectType>

type ValidateSchema<T> =
    T extends StringSchema ? StringSchema :
    T extends readonly ["object", ObjectConstraints, infer S extends ObjectType] ?
        readonly ["object", ObjectConstraints, { [K in keyof S]: ValidateSchema<S[K]> }] :
    never

function validate<const T extends AnySchema>(schema: T & ValidateSchema<T>, value: any) {
    return []
}

// Ok: context: ValidationContext<StringType>
validate(["string", { length: (context) => 1 }], null)

// Error: Parameter 'context' implicitly has an 'any' type
validate(["string", { required: (context) => true }], null)

// Error: Parameter 'context' implicitly has an 'any' type
validate(["object", { required: (context) => true }, {}], null)

// Ok:
//     required: true | (true & ((context: ValidationContext<ObjectType>) => boolean))
// and
//     required: true | (true & ((context: ValidationContext<StringType>) => boolean))
validate(["object", { required: true }, {
    name: ["string", { required: true }],
}], null)

虽然在第一个

context
调用中正确推断了
validate
参数的类型(即:
ValidationContext<StringType>
约束),但一旦我使用约束名称
length
,它就会变成
any
required
StringConstraints
分享。
任何人都可以帮我获得预期的

ObjectConstraints

类型吗?

这里有一个 

typescript Playground 链接

,其中包含上面的代码。


编辑

(简化版本,游乐场链接此处): ValidationContext<...>

如果我将模式定义与对象一起使用(
playground

),问题就消失了: type StringSchema = ["string", { required?: (value: string) => boolean }] type NumberSchema = ["number", { required?: (value: number) => boolean }] type AnySchema = StringSchema | NumberSchema function validate(schema: AnySchema) { return schema[0] } // Error: Parameter 'value' implicitly has an 'any' type validate(["string", { required: (value) => true }]) // Error: Parameter 'value' implicitly has an 'any' type validate(["number", { required: (value) => true }])

所以我们可以非常确定这与元组的使用有关。

我应该修复 Github 上的错误吗?

如果我能理解如何设置 tsc 的自定义版本,我会尝试提供 PR。

typescript
1个回答
0
投票
microsoft/TypeScript#55632

。 TypeScript 目前无法为数组文字中的元组类型可区分联合根据上下文键入回调参数。 事实上,如果您从数组切换到对象,您将获得您期望的上下文类型。 目前的解决方法是放弃上下文类型(并且只是注释您的回调参数)或放弃元组(并使用对象代替)。 此错误被标记为

“需要帮助”

,因此 TS 团队欢迎拉取请求来修复它。 感兴趣的读者希望看到这个问题被修复而不是解决它,可能会决定提交一个修复它的 PR。

© www.soinside.com 2019 - 2024. All rights reserved.