Skip to content

Extending mapped type doesn't allow extra properties when extended type has string literal values #27707

@asmundg

Description

@asmundg

TypeScript Version: 3.2.0-dev.20181011

Search Terms: extends mapped literal widening

Code

// Type '{ a: string; c: number; }' is not assignable to type '{ a: "a"; }'.
//   Object literal may only specify known properties, and 'c' does not exist in type '{ a: "a"; }'. [2322]
const f: <T extends { [P in keyof T]: { a: "a" } }>(literal: T) => void = () => {}
f({one: {a: "a", c: 1}})

// works
const a: "a" = "a"
f({one: {a, c: 1}})

// works
const f_: <T extends { [P in keyof T]: { a: string } }>(literal: T) => void = () => {}
f_({one: {a: "a", c: 1}})

// works
const f__: <T extends {one: { a: "a" }}>(literal: T) => void = () => {}
f__({one: {a: "a", c: 1}})

Expected behavior:

Code compiles for all cases.

Actual behavior:

Code fails when extending a mapped type with string literal properties:

test.ts:4:18 - error TS2322: Type '{ a: string; c: number; }' is not assignable to type '{ a: "a"; }'.
  Object literal may only specify known properties, and 'c' does not exist in type '{ a: "a"; }'.

Removing either the mapping, the string literal or the widenable input makes the code pass type checking.

Also notable is that the one.a property of the function argument is only inferred to be defined by the extended type in the first case. E.g. jumping to definition only does something in the first f() call.

This seems related to the a property being widened when mapped over.

Playground Link: http://www.typescriptlang.org/play/#src=%2F%2F%20Type%20'%7B%20a%3A%20string%3B%20c%3A%20number%3B%20%7D'%20is%20not%20assignable%20to%20type%20'%7B%20a%3A%20%22a%22%3B%20%7D'.%0D%0A%2F%2F%20%20%20Object%20literal%20may%20only%20specify%20known%20properties%2C%20and%20'c'%20does%20not%20exist%20in%20type%20'%7B%20a%3A%20%22a%22%3B%20%7D'.%20%5B2322%5D%0D%0Aconst%20f%3A%20%3CT%20extends%20%7B%20%5BP%20in%20keyof%20T%5D%3A%20%7B%20a%3A%20%22a%22%20%7D%20%7D%3E(literal%3A%20T)%20%3D%3E%20void%20%3D%20()%20%3D%3E%20%7B%7D%0D%0Af(%7Bone%3A%20%7Ba%3A%20%22a%22%2C%20c%3A%201%7D%7D)%0D%0A%0D%0A%2F%2F%20works%0D%0Aconst%20a%3A%20%22a%22%20%3D%20%22a%22%0D%0Af(%7Bone%3A%20%7Ba%3A%20a%2C%20c%3A%201%7D%7D)%0D%0A%0D%0A%2F%2F%20works%0D%0Aconst%20f_%3A%20%3CT%20extends%20%7B%20%5BP%20in%20keyof%20T%5D%3A%20%7B%20a%3A%20string%20%7D%20%7D%3E(literal%3A%20T)%20%3D%3E%20void%20%3D%20()%20%3D%3E%20%7B%7D%0D%0Af_(%7Bone%3A%20%7Ba%3A%20%22a%22%2C%20c%3A%201%7D%7D)%0D%0A%0D%0A%2F%2F%20works%0D%0Aconst%20f__%3A%20%3CT%20extends%20%7Bone%3A%20%7B%20a%3A%20%22a%22%20%7D%7D%3E(literal%3A%20T)%20%3D%3E%20void%20%3D%20()%20%3D%3E%20%7B%7D%0D%0Af__(%7Bone%3A%20%7Ba%3A%20%22a%22%2C%20c%3A%201%7D%7D)%0D%0A

Related Issues:

Metadata

Metadata

Assignees

Labels

BugA bug in TypeScriptDomain: Mapped TypesThe issue relates to mapped typesNeeds More InfoThe issue still hasn't been fully clarified

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions