Skip to content

Broken inference between index typed object and Record #14930

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

Open
AlexGalays opened this issue Mar 30, 2017 · 4 comments
Open

Broken inference between index typed object and Record #14930

AlexGalays opened this issue Mar 30, 2017 · 4 comments
Labels
In Discussion Not yet reached consensus Suggestion An idea for TypeScript

Comments

@AlexGalays
Copy link

TypeScript Version: 2.2.2

Code

interface Errors {
  [field: string]: {
    key: string
    message: string
  }
}

const errors: Errors = {}

function genericObjectFunction<K extends string, V>(obj: Record<K, V>): [K, V][] {
  return []
}

/*
 Argument of type 'Errors' is not assignable to parameter of type 'Record<string, never>'.
 Index signatures are incompatible.
 Type '{ key: string; message: string; }' is not assignable to type 'never'.
 */
genericObjectFunction(errors)

The types seem generally compatible though, as expected this is legal:

const errorRecord: Record<string, { key: string, message: string }> = errors

Expected behavior:
genericObjectFunction(errors) should compile.

@aluanhaddad
Copy link
Contributor

aluanhaddad commented Mar 30, 2017

The reason this fails is that

interface Errors {
  [field: string]: {
    key: string
    message: string
  };
}

has no discrete keys. So the record is empty. The Record<K, V> type is defined by mapping keys to values and since there are no known values, the generic instantiation has no members.

Consider

const errors = { // no explicit type, inferred type has members, still compatible with interface.
  password: {
    key: 'minimumComplexity',
    message: 'Password must contain at least one non alphanumeric character'
  }
};

function genericObjectFunction<K extends string, V>(obj: Record<K, V>): [K, V][] {
  return [];
};

const genericErrors = genericObjectFunction(errors);

EDIT:
Here is an example with a workaround (note: the overload order is significant)

interface Errors {
  [field: string]: {
    key: string
    message: string
  }
}

const errors: Errors = {
  password: {
    key: 'minimumComplexity',
    message: 'Password must contain at least one non alphanumeric character'
  }
};

function genericObjectFunction<K extends string, V>(obj: Record<K, V>): [K, V][];
function genericObjectFunction<V>(obj: { [field: string]: V }): [string,{[P in keyof V]:V[P]}][];
function genericObjectFunction<K extends string, V>(obj: Record<string, V>): [K, V][]{
  return [];
};
const typeInferredErrors = {
  password: {
    key: 'minimumComplexity',
    message: 'Password must contain at least one non alphanumeric character'
  }
};

const genericErrorsX = genericObjectFunction(errors);
const genericErrorsY = genericObjectFunction(typeInferredErrors);

@AlexGalays
Copy link
Author

Yes, I tried the overloads as a POC and it works. It's just a bit annoying to do that on dozens of functions :)

I understand Record tries to extract information from explicit keys and values, but couldn't a simple translation exist between the two worlds?

@aluanhaddad
Copy link
Contributor

I understand Record tries to extract information from explicit keys and values, but couldn't a simple translation exist between the two worlds?

Possibly, but I feel like mixing the two could be error prone.

It is also possible that this is a bug. I do not believe it is but I could certainly be wrong.

@RyanCavanaugh RyanCavanaugh added the Needs Investigation This issue needs a team member to investigate its status. label May 24, 2017
@mhegazy mhegazy added Bug A bug in TypeScript and removed Needs Investigation This issue needs a team member to investigate its status. labels Feb 8, 2018
@mhegazy mhegazy added Suggestion An idea for TypeScript In Discussion Not yet reached consensus and removed Bug A bug in TypeScript labels Jul 23, 2018
@russelldavis
Copy link
Contributor

This no longer produces an error as of v3.3.3.

Playground link: v3.1.6, with error
Playground link: v3.3.3, no error

Should this be closed?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
In Discussion Not yet reached consensus Suggestion An idea for TypeScript
Projects
None yet
Development

No branches or pull requests

5 participants