Skip to content

Inconsistent compiler behavior with recursive conditional type as variables or parametersΒ #60221

Closed
@fwolff

Description

@fwolff

πŸ”Ž Search Terms

recursive conditional type compilation variable parameter

πŸ•— Version & Regression Information

  • This is the behavior in every version I tried, and I reviewed the FAQ for entries about recursive conditional types

⏯ Playground Link

https://www.typescriptlang.org/play/?#code/FAFwngDgpgBAyiATgSwHYHMDCB7VBnJAQzRDxgF4YBvGRKARwFdk6ATAfgC4YAjbbADZRCqGAF9QkWAHkeAKygBjEDnxESZSjTpMWUDtz6DhoiZOjwkadHEUALKAFtCFGAG0ARARQYPAGksfLFxvYlRSAF1zGXklEFsHZwAeABUYKAAPEChUVjJsWOUYAB8YVEYBARKYRlyoADM0fQA+V08ChWV-ancABRg0GABrKDBsepgUiO4Ep0IAMWxEVLdeiNaxKOj4eznF5ZTWymAYU-cp9KycvPdva2ryyura1gam1giYdkDrWecYTgnM5uC6ZbK5MhuDpxB4VKqlF5vVD6T7fWSdeK7ZKHAFA07IgBuUEQwG2AEFUGA-i5KAggtTqui4tSklQxM1SeALL1iXhcK4qHiyoRHFBuHcMLDKsAzPVaspkPyCYQBMhWIRsgAKPBYwjcClU3UASmoQroIEYiFEbiiZkUIRAMB1iUIAEY2h5oV0AoKzsLRdxPBL0N1tAxmGxuEhGLBNn4ZVt7WonbqAEz6ykMyjtQogUNC1AisXuLxWXw+2jhvSsKOIGPiCLxzakpMEFMugDMM11+ySPMQfNQRxLXrzPoLRcDpaCocrukjMGjscbCdJytV6q1zrmrqNwAA9PuYABRRCIJb6xDoRii8IwcaLqQwADk2rL6GqNELAZgb6Cn7nCN9EMfghBEABucQjRtSCxGgiJnwGMhUGwR1CDwPBkHQQseCERdsBgCBCEQItskQe8Ji5WBnwNalnwAOgPI9JifV9gwA79iz-e5SjDedgN4UCTFg+CRJtRDkGQ1CYHQzDsMIXDYBAAiqJfJllBZNlmgYpi-RSYj0CgR0dCAshU3SIRbxAbUTR4RhHT5S1FFgZwwBgOxCCJGB6igAB3Yl6M1VMOwAFgAVj3YB1zVDUoG1NMTUPGAADkVKfXZFCGawAk4mTckA6sYEUEReBcyToEqfQopVGKt11DtEqPAB1JYhjIdDLmgZR9ACVSMqyyVJPvIZqo3WLNRzDFZySlrEDawjiJAZAVQEMBevShxMvuXzWvyURPVzDxeHssppP5adrCOpYYA8Ezqw8CcfyDd9ZzuhclwbJsIkioA

πŸ’» Code

type StringConstraints = { required?: boolean }
type ObjectConstraints = { required?: boolean }

type StringSchema = ["string", StringConstraints]
type ObjectSchema<T extends object | null | undefined> = ["object", { [P in keyof T]: SchemaFor<T[P]> }]

type SchemaFor<T> = 
    [T] extends [string | null | undefined] ? StringSchema :
    [T] extends [object | null | undefined] ? ObjectSchema<T> :
    never

type AnySchema = StringSchema | ObjectSchema<{}>

type Person = {
    name: string | null
}

function validate(schema: AnySchema) {
    return []
}

const schema1 = ["object", {
    name: ["string", { required: true }],
}]

const schema2: AnySchema = ["object", {
    name: ["string", { required: true }],
}]

const schema3: SchemaFor<Person> = ["object", {
    name: ["string", { required: true }],
}]

validate(schema1)
// Error: Argument of type '(string | { name: (string | { required: boolean; })[]; })[]' is not assignable to parameter of type 'AnySchema'.
//  Type '(string | { name: (string | { required: boolean; })[]; })[]' is not assignable to type 'ObjectSchema<{}>'.
//    Target requires 2 element(s) but source may have fewer.(2345)

validate(schema2) // No type checking, name and required can be mispelled
validate(schema3) // Works as expected, type checking is ok
validate(["object", { // Works partially, type checking works on "object" but not on "string" or "required"
    name: ["string", { required: true }],
}])

πŸ™ Actual behavior

Type-checking only works with schema3, where all properties have to be correctly spelled. schema1 gives a compiler error (see code). schema2 can be completely misspelled with no errors. Passing directly a schema as parameter to the validate function works partially.

πŸ™‚ Expected behavior

All four schema definitions should work with correct and complete type-checking.

Additional information about the issue

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    QuestionAn issue which isn't directly actionable in code

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions