Skip to content

Type parameter's function properties referencing other properties of the same parameter are brokenΒ #60848

Closed as not planned
@ghost

Description

πŸ”Ž Search Terms

generics, type parameters, dependent properties

πŸ•— Version & Regression Information

None of the versions I have tried work as expected.

⏯ Playground Link

https://www.typescriptlang.org/play/?ts=5.1.6#code/HYQwtgpgzgDiDGEAEAnA9gV2AEwPpuGQG8AoJcpAekqQBsIAXAciiSgZBQaQHcBLBgAskIJADMs8BnwJIhIBgC4yFakgC0I+IhgNWo4Bkgo+8JAAMYJsJwCeABXQwIXW+ZXk1mhDr0XsEM44EMAMjmjOrua8gqbCUIKYtNhIAEbIfAGhpiC0cmgWVnw2KA5OLgxuHlQ03tqBfhHSBLn+gSFZDABiks3A4ZGV0Tyx8MJ8+uK9MsC8AsKicCjgjC5pIFAQKbKW1nYDFVUUSAHwtJzIEsBSMycQYnyEADzwBOxIaKkAVkgQAB4MDqsUjHY4oCAgbAEWi2JBFEplCKHRRIQxgdIoarHAJBToHVwoz5fADaTHh+3KriYAF0sRQcR0Qt1pgR8ZUAPwogAU5NKhO+pN5iMGthpAEokABeAB8SAAbmhMtUAL7SrlE-lfMWapAkapqKAFMScX5yly2ISPADmvDQKAA1vpWP9nFIttUAg9CFykCDQeQhWzbCiAIwhgA0dPIDOCoSDoYjNVRBRc6BQUBVSDFHvujwgPr9-sDlMqCcj-uj7VjYRLwaQIYAzOGkyBZqm7Zns9jc97fVG4XtSvH6xH+zG8bWy2Oq50etc+sOuRKZb7lc21MAUyg06w+GI5IIXBlWJu4ZwVoDMcdlVm9cc1KkMNwRiFeMhISlFufIJf8m1cUyc43KytbmM2AhpOCICOmkEDwCAGCbAAdChOZevmfYVgOxQUkiBIjuWFbjkyw5hoR-pqAAKrYzhIEwPKDnWaIYsusoKpkTBIBMybcBsUB8FaoCpPQf6VLR9FCiiWD2puPDAKx8qKtgTBIbqWFqEImBWsIPDINgmTAEw3DsAoyAjHEcg0WZyAunB3CPAeyBLBeLgAIQiDgMSwvpKS7DhpTROkryQBY0mycA5jstOAGhEBC6TkgDH+bYCmFlh2EIv2a6diQyp6qAkCwAgyDoFgeAMDwBRpWoe5vqggTnIgIhTPOtxWHhlRzEIzU-ok2DNpawBWqwVqMKar66XwKABCgSE5mcFwtcBsyenmLxvNwRK-ACQKYRWUFQsAMIZbhIoosxLjRYycYJUSgqMUGNJXdWACyjB9UlCKavdyWPdS2qKUq15qhqHzfADW13vSPYYWlxzFh1dZkc9E6I1OWHEaEb2adgn12BKcMVmofkItE3GbvZwBiKmWwiKw5hhWgcnRLJzaPsZ0i0Hk5NoNw5hhuYKICCwzXmBdKDuOlFBCll5FIDe2b6jQ7N1fBsw2IYuTHa2FOmYUjHDPMmC8cAsLtcJEBgBm940OZYy2hgySwVxYAwGgUD8Rb4h2kgABEm4oDYtC+0tfRsAJoAMBg4LOn8rrSENFji5L0PoQW-YI2dBEoyRCXIxjM5MtjH2SSOBNJqe7bpv28OMbLOVKx5usJzaJN2Ib3WGr11onHu1PgqEB6tiO9VQI7fgOcPVd02+XNoXm6dYZnyLZwXMU1mjq9EYXWPvWguOlyGABM5dqFP24dlLAZ11h2XXreeUkAV0BwE1pU4LgQjgsQjeyXVo3cAYAUIQ1kpoBHeEsTa+4QEDk+PQMAc0bZaHgJkJkWtYR8CYGaXuUBXhmnBCkeQxligwGOuCZwChrSNxgacc4yww5oH3OYVahBog2HtNASyzhcEmF0LBQQIBsG5DAO7bgrxtx2RhPNOhlwWQrRhutYA7wtr-EBDgYE-YDrQjNg9BK4sc43U3ndMkujEZPTXtdZkrUQKI05IlUuxjl5Un+lKNiSkVQg2+JqCG3xpGLSuMtO46FFHKO+NtNR2ANFYS0UdHRv09FGAxAYjeWdHGmJFOY7e684ozCDHYvGfIwYkhMfEsxLiVzsWwB49UXiik+J+FDSsac9pFnSSvfOWTLGkVHOpGglcL7piQIecEbMnxDLQPgnuEy1htwCmwRIPB6bJxnrVXSb5wR3AWiYROzCd5WOWkGaIGwep7yqRY6sOSbFZwKSlVxLT0oy1vnLBWjSWzAGbj3WZbhbQOlYLQPgHCuKzF2evYu+8jmsF0nPbszTCbX1KVnDp-pMYpPaT0zpFy5GLkPiGVKFctw7hrtLG+FY74UBeTChe9za4IrRXLbs69un0uhtkrFCUbmhhPncogbzfgDKJfCzKTycrKiAA

πŸ’» Code

namespace round_one {
    // let's start with declaring a function that:
    // - accepts a numeric `primaryProperty`
    // - accepts `dependentProperty` which should be identical to `primaryProperty`
    // - accepts optional `dependentFunctionProperty` which is a function with a parameter based on `primaryProperty`
    declare function define<const obj extends {
        readonly primaryProperty: number
        dependentProperty: obj['primaryProperty']
        dependentFunctionProperty?: (primary: obj['primaryProperty']) => void
    }>(obj: obj): obj 

    // so far everything works as expected
    define( {
        primaryProperty: 11,
        dependentProperty: 11, // no errors
    } )
    define( {
        primaryProperty: 11,
        dependentProperty: 13, // an error
    } )
    define( {
        primaryProperty: 11,
        dependentProperty: 11,
        dependentFunctionProperty: () => {}, // no errors if there is no parameter
    } )

    // but when we add a parameter to `dependentFunctionProperty`, it breaks because...
    define( {
        primaryProperty: 11,
        dependentProperty: 11,
        // Type '(primary: number) => void' is not assignable to type '(primary: unknown) => void'. 
        // though we didn't state which type we expect in the parameter! and why did `primary` become `unknown`?
        dependentFunctionProperty: (primary) => {
            primary
        },
    } )
}

namespace round_two {
    // if we replace a function property with a method, things get even weirder.
    declare function define<const obj extends {
        readonly primaryProperty: number
        dependentProperty: obj['primaryProperty']
        dependentMethod(primary: obj['primaryProperty']): void
    }>(obj: obj): obj 

    define( {
        primaryProperty: 11,
        dependentProperty: 11,
        dependentMethod(primary) {
            // `primary` is not inferred as `unknown` now, but still is not `11`: it's a `number`
            primary
        },
    } )

    // but we can manually annotate `primary` without any problems
    // which would be impossible for "normal" function signatures expecting `number`
    define( {
        primaryProperty: 11,
        dependentProperty: 11,
        dependentMethod(primary: 11) { // no errors
            primary
        },
    } )

    // annotating `primary` with something different than 11 results in an error as well
    define( {
        primaryProperty: 11,
        dependentProperty: 11,
        dependentMethod(primary: 12) { // an error
            primary
        },
    } )
}

namespace round_three {
    // now we get to the weirdest part of the problem.
    // accidentally i've discovered that simply repeating
    // the declaration of `define` makes typescript behave almost correctly
    declare function define<const obj extends {
        readonly primaryProperty: number
        dependentProperty: obj['primaryProperty']
        dependentFunctionProperty?: (primary: obj['primaryProperty']) => void
    }>(obj: obj): obj
    declare function define<const obj extends {
        readonly primaryProperty: number
        dependentProperty: obj['primaryProperty']
        dependentFunctionProperty?: (primary: obj['primaryProperty']) => void
    }>(obj: obj): obj 

    define( {
        primaryProperty: 11,
        dependentProperty: 11,
        // no errors here, but hovering over `primary` shows `number` as if we were declaring `dependentFunctionProperty` as a method
        dependentFunctionProperty: (primary) => {
            primary
        },
    } )

    // annotating `primary` works like in `dependentMethod` as well
    define( {
        primaryProperty: 11,
        dependentProperty: 11,
        dependentFunctionProperty: (primary: 11) => { // no errors
            primary
        },
    } )
    define( {
        primaryProperty: 11,
        dependentProperty: 11,
        dependentFunctionProperty: (primary: 12) => { // an error
            primary
        },
    } )
}

πŸ™ Actual behavior

I'm not sure where to begin. Properties of a type parameter that reference other properties of the same type parameter seem to be completely untested in TypeScript.

In the example above, I have referenced a generic's property from other properties in three ways:

  • from a function property
  • from a method
  • from a function property, but declaration of the function is duplicated!

All of them are buggy, but in different ways.

πŸ™‚ Expected behavior

Speaking honestly, I was expecting from such dependent properties to not work at all, failing with some kind of circular constraint error, but now I believe they could become usable some day.

Additional information about the issue

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    DuplicateAn existing issue was already created

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions