Skip to content

Unsafe assignment of Record into interface with optionals #40529

Closed
@villasv

Description

@villasv

Search Terms:
typescript unsafe Record unknown assignment interfaces all optional undefined sound type system

Code

I discovered the following code to be legal. It was pretty scary.

interface ObjWithPropFalsy {
    prop?: false;
}

const a: Record<string, boolean> = { prop: true };
const b: ObjWithPropFalsy = a;

if (b.prop) { // b.prop is of type false | undefined, but its value is true
    console.log("typing says it never happens") // <-- will print
} else {
    console.log("typing says it always happens")
}

This is so dangerous for me* that I decided to propose a new eslint rule warning about it, but @bradzacher suggested I brought this issue to the TypeScript team. He also added the following example, which is even more intense because unknown isn't really relevant here:

interface ObjWithPropFalsy {
    prop?: false;
}

const a: Record<string, string> = { prop: 'str' };
const b: ObjWithPropFalsy = a;

type T = (typeof b)['prop']; // === false | undefined;
typeof a.prop === 'string' // runtime

Expected behavior:
Compilation error.

Actual behavior:
Compilation successful.

*(can explain the use-case that led to the discovery if it's relevant)

I understand this might be an inevitable design limitation coming from index signatures, so this might be closer to a feature request than a bug? If this is the case, sorry for the wrong labeling.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Design LimitationConstraints of the existing architecture prevent this from being fixed

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions