Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cannot assign an instance of the Intersection of a Record and a Partial of a Record #44196

Closed
CamilleDrapier opened this issue May 21, 2021 · 4 comments
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug

Comments

@CamilleDrapier
Copy link

CamilleDrapier commented May 21, 2021

Bug Report

Defining an object as an intersection of a Record and a Partial of a Record with a key more specific and a different value type does not allow to set the instance object with the Partial type.

Not completely sure if this is a bug or a design limitation. 🙇

🔎 Search Terms

  • Record intersection partial

Might be related to: #17867, #38977,

🕗 Version & Regression Information

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

⏯ Playground Link

Playground link with relevant code ; edited

💻 Code

type FuzzyObject = Record<string, string> & Partial<Record<"arrayProperty", string[]>>;

let fuzzy: FuzzyObject = {}; // works
fuzzy = { abc: "abc" }; // works
fuzzy.abc = "abc"; // works
fuzzy.arrayProperty = []; // works
fuzzy.arrayProperty = ["abc"]; // works

const abc = fuzzy.abc; // works and is correctly infered as `string`
const arrayProperty = fuzzy.arrayProperty; // works and is correctly infered as `string[]`

fuzzy = { arrayProperty: ["abc"] }; // does not work: `Type 'string[]' is not assignable to type 'string'.(2322)`
fuzzy = { arrayProperty: ["abc"] } as FuzzyObject; // works
fuzzy = { abc: "abc", arrayProperty: ["abc"] }; // does not work: `Type 'string[]' is not assignable to type 'string'.(2322)`
fuzzy = { abc: "abc", arrayProperty: ["abc"] } as FuzzyObject; // does not work: `Type 'string[]' is not comparable to type 'string'.(2352)`
fuzzy = { abc: "abc", arrayProperty: ["abc"] } as any as FuzzyObject; // works
fuzzy = { abc: "abc", arrayProperty: "abc" }; // expected error: `Type 'string' is not assignable to type 'string[] | undefined'.(2322)`

type ReverseFuzzyObject = Partial<Record<"arrayProperty", string[]>> & Record<string, string>;

let reverseFuzzy: ReverseFuzzyObject = {}; // works
reverseFuzzy = { abc: "abc" }; // works
reverseFuzzy.abc = "abc"; // works
reverseFuzzy.arrayProperty = []; // works
reverseFuzzy.arrayProperty = ["abc"]; // works

const reverseFuzzyAbc = reverseFuzzy.abc; // works and is correctly infered as `string`
const reverseFuzzyArrayProperty = reverseFuzzy.arrayProperty; // works and is correctly infered as `string[]`

reverseFuzzy = { arrayProperty: ["abc"] }; // does not work: `Type 'string[]' is not assignable to type 'string'.(2322)`

🙁 Actual behavior

The following does not work:

type FuzzyObject = Record<string, string> & Partial<Record<"arrayProperty", string[]>>;
const fuzzy: FuzzyObject = { abc: "abc", arrayProperty: ["abc"] }; 

Has the error:

Type '{ arrayProperty: string[]; }' is not assignable to type 'FuzzyObject'.
  Type '{ arrayProperty: string[]; }' is not assignable to type 'Record<string, string>'.
    Property 'arrayProperty' is incompatible with index signature.
      Type 'string[]' is not assignable to type 'string'.(2322)

🙂 Expected behavior

The above should work with no error.

@KristjanTammekivi
Copy link

I guess it's related to why arrayProperty isn't allowed in

interface FuzzyObject {
    arrayProperty?: string[]; // Property 'arrayProperty' of type 'string[] | undefined' is not assignable to string index type 'string'.(2411)
    [key: string]: string;
}

@RyanCavanaugh RyanCavanaugh added the Working as Intended The behavior described is the intended behavior; this is not a bug label May 27, 2021
@RyanCavanaugh
Copy link
Member

This the intended behavior. To be assignable to a T & U, the source needs to be assignable to both T and U (this is sort of the definition of intersection). Record<string, string> does not permit the source to have any declared properties of type other than string, so the assignment is rejected.

@CamilleDrapier
Copy link
Author

Thank you for taking the time to provide explanations.

I see, it seems very reasonable. But isn't this behavior somewhat inconsistent? (the fact that for example I can set/read the arrayProperty even if nothing can honor its type in theory?) It seems like once I set the fuzzy: FuzzyObject variable, it doesn't really behave like an intersection of type but like the the Partial<Record<"arrayProperty", string[]>> overrides the base definition (which is what I originally intended to achieve, maybe unreasonably).

I think this might have to do with the following behavior difference:

type NeverType = number & boolean; // Resolves to "never"
const neverConst: NeverType = 1; // expected error: `Type 'number' is not assignable to type 'never'.`

type NearlyNeverType = string & string[]; // Resolves to string & string[]
const nearlyNeverConst: NearlyNeverType = "1"; // Expected error, but message says: `Type 'string' is not assignable to type 'string[]'` (and vice-versa when assigning an array)

I suppose that NearlyNeverType is not resolved to never because string[] is not a primitive type; and it sounds like if it was an easy problem to solve, it would have been done already.

@typescript-bot
Copy link
Collaborator

This issue has been marked 'Working as Intended' and has seen no recent activity. It has been automatically closed for house-keeping purposes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug
Projects
None yet
Development

No branches or pull requests

4 participants