Skip to content

[4.9.5] Destructuring type inference fails on direct assignment, succeeds with intermediate variable #52629

Closed
@tminich

Description

@tminich

Bug Report

Full details in the playground link / below, summary:

export function setupImages<R extends ImageHolder<K>, K extends string>(item: R, keys: K[]): SetupImages<K> {
    return <any>undefined
}

const test: TestInterface = <any>undefined

const two_steps = setupImages(test, ['image'])
const {prepare: prepare_two_steps, ...two_steps_rest} = two_steps

const {prepare: prepare_one_step, ...one_step_rest} = setupImages(test, ['image']) // Error on `test`

The two step version works, the one step version fails with

Argument of type 'TestInterface' is not assignable to parameter of type 'ImageHolder<string>'.
  Type 'TestInterface' is not assignable to type 'ImageRefHolder<string>'.
    Index signature for type '`${string}_ref`' is missing in type 'TestInterface'.(2345)

so apparently narrowing down K fails.

🔎 Search Terms

Destructure, Destructuring assingment

🕗 Version & Regression Information

  • This is the behavior in every version I tried (down to 4.5.5)

⏯ Playground Link

Playground link with relevant code

💻 Code

Tried to slim it down, but unfortunaely it's still a bit complex

export function setupImages<R extends ImageHolder<K>, K extends string>(item: R, keys: K[]): SetupImages<K> {
    return <any>undefined
}

const test: TestInterface = <any>undefined

const two_steps = setupImages(test, ['image'])
const {prepare: prepare_two_steps, ...two_steps_rest} = two_steps

const {prepare: prepare_one_step, ...one_step_rest} = setupImages(test, ['image'])


/********
 * Types
 */


export interface RouteRef {
    uuid: string
}

export interface FileItem {
    file: File
}

type ImageRefHolder<K extends string> = {
    [P in `${K}_ref`]: RouteRef
}
type ImageUuidHolder<K extends string> = {
    [P in `${K}_uuid`]: string
}
export type ImageHolder<K extends string> = ImageRefHolder<K> & ImageUuidHolder<K>
export type ImagesPrepared<I extends ImageHolder<K>, K extends string> = Omit<I, ImageOmitKeys<K>[number]> & ImageFinalHolder<K>
type ImageOmitKeys<K extends string> = (keyof ImageHolder<K>)[]
type ImageFinalFile<K extends string> = {
    [P in K]?: File
}
type ImageFinalDelete<K extends string> = {
    [P in `${K}__delete`]?: 1
}
type ImageFinalHolder<K extends string> = ImageFinalFile<K> & ImageFinalDelete<K>


type SetupImageRefsItem<K extends string> = {
    [P in K]: FileItem
}

interface PrepareImages<K extends string> {
    prepare: <R extends ImageHolder<K>>(data: R) => ImagesPrepared<R, K>
}

type SetupImageRefs<K extends string> = SetupImageRefsItem<K>

type SetupImages<K extends string> = SetupImageRefs<K> & PrepareImages<K>

export interface TestInterface {
    name: string
    image_ref: RouteRef
    image_uuid: string
}

🙁 Actual behavior

Error on the indicated line

🙂 Expected behavior

No error as type resolution appears to work fine in the version with an intermediate variable. The one line version should imo be fully equivalent.

Metadata

Metadata

Assignees

No one assigned

    Labels

    BugA bug in TypeScript

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions