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

Template literal types fail in mapped types #42192

Closed
hugonteifeh opened this issue Jan 3, 2021 · 13 comments · Fixed by #44512
Closed

Template literal types fail in mapped types #42192

hugonteifeh opened this issue Jan 3, 2021 · 13 comments · Fixed by #44512
Assignees
Labels
Bug A bug in TypeScript Fix Available A PR has been opened for this issue

Comments

@hugonteifeh
Copy link

hugonteifeh commented Jan 3, 2021

TypeScript Version: 4.1.3

Search Terms: template literal types

type Pseudo = `&:${string}` 

const AmIPseudo1: Pseudo = '&:test' // Passes as expected
const AmIPseudo: Pseudo = '&' // Fails as expected

type PseudoDeclaration = {
    [key in Pseudo]: string
}

// No Errors
const test: PseudoDeclaration = {
    'someKey' : 'someValue'
}

Expected behavior:

test should not be able to pass the type-checking as its key 'someKey' is not of type Pseudo.

Actual behavior:

test passes the type-checking.

@hugonteifeh hugonteifeh changed the title Template literals fails in mapped types Template literal types fail in mapped types Jan 3, 2021
@RyanCavanaugh RyanCavanaugh added the Needs Investigation This issue needs a team member to investigate its status. label Jan 4, 2021
@RyanCavanaugh
Copy link
Member

@ahejlsberg the output from this is fairly useless compared to expectation; should we make it an error?

@ahejlsberg
Copy link
Member

This should be fixed by #26797.

@KostyaTretyak
Copy link

KostyaTretyak commented Feb 6, 2021

Same issue with TypeScript v4.1.2:

type FieldPattern = `/${string}`;

// Works as expected
const path1: FieldPattern = '/one'; // OK
const path2: FieldPattern = 'two'; // Type '"two"' is not assignable to type '`/${string}`'.ts(2322)

type PathsObject = { [P in FieldPattern]: object; };
const pathObject: PathsObject = 123; // No error. Why? TypeScript ignore any values.

@KostyaTretyak
Copy link

I found a workaround:

type FieldPattern = `/${string}`;

type PathsObject<T extends FieldPattern = any> = { [P in T]: object; };
const pathObject1: PathsObject = 123; // Error as expected
const pathObject2: PathsObject<'/one'> = {'/one': {}}; // OK

But I would like, of course, to have without bugs the version that I showed in the previous example.

@loreanvictor
Copy link

loreanvictor commented Feb 17, 2021

on this front maybe it is worth mentioning that types with such indexes also display other unexpected behaviors:

type A = `A${string}`
type Akey = { [key in A]: string }
const a: Akey = { 'Ak': 'ira' }
a['Ak'] = 'ira'       // --> this results in unexpected error

playground

I am unsure whether this is due to the same issue or is a different issue.

@brandon-leapyear
Copy link

brandon-leapyear commented Mar 27, 2021

This is an old work account. Please reference @brandonchinn178 for all future communication


In the following example, barBad1 should be rejected because for a given string, it should require both -end1 and -end2. barBad2 should be rejected because asdf does not match any of the template literals. But neither give an error

// all of these work as expected
type Foo = `${string}-${'end1' | 'end2'}`
let fooGood1: Foo = 'asdf-end1'
let fooGood2: Foo = 'asdf-end1'
let fooBad: Foo = 'asdf'

// Typescript evaluates this to `type Bar = {}`
type Bar = {
    [K in Foo]: null
}
let barGood1: Bar = {
    'asdf-end1': null,
    'asdf-end2': null,
}
let barGood2: Bar = {
    'asdf-end1': null,
    'asdf-end2': null,
    'hello-end1': null,
    'hello-end2': null,
}

// these unexpectedly pass
let barBad1: Bar = {
    'asdf-end1': null,
}
let barBad2: Bar = {
    'asdf': null,
}

playground

@Sharcoux
Copy link

Sharcoux commented Apr 9, 2021

Is this the same error?

type IdType = `${number}-${number}-${number}-${number}`
const id: IdType = '0000-0000-0000-0001';

type A = Record<IdType, string>

const a: A = {
    [id]: 'test'
}
a[id] // Element implicitly has an 'any' type because expression of type '`${number}-${number}-${number}-${number}`' can't be used to index type 'A'.(7053)

@ezsh
Copy link

ezsh commented Apr 11, 2021

Also, given let o: Record<AStringTemplateType, Whatever>, Object.entries(o) return type is [string, unknown][].

@jcalz
Copy link
Contributor

jcalz commented May 19, 2021

Do we expect #26797 to ever be merged? If not, should this be fixed some other way? Or closed as a design limitation?

@typescript-bot typescript-bot added the Fix Available A PR has been opened for this issue label Jun 8, 2021
@ahejlsberg ahejlsberg added Bug A bug in TypeScript and removed Needs Investigation This issue needs a team member to investigate its status. labels Jun 16, 2021
@jacobdr
Copy link

jacobdr commented Jun 21, 2021

Sorry for asking perhaps a stupid question, but does this now allow computed properties on class definitions? Something like:

class Foo {
    [key in 'foo' | 'bar']: string;
}

or

class Foo {
    [key: 'foo' | 'bar']: string;
}

@ahejlsberg
Copy link
Member

Implemented in #44512 which is now in the main branch.

@ahejlsberg
Copy link
Member

@jacobdr With #44512 we're a lot more flexible with respect to index signatures. However, we don't permit key types in index signatures to be literal types such as 'foo' | 'bar' as that really is equivalent to two individual properties (which you could just declare).

@jacobdr
Copy link

jacobdr commented Jun 22, 2021

@ahejlsberg Thanks for the response... Our use case is admittedly weird.

Some of the libraries we are using in our stack use metadata reflection to generate Swagger definitions or ORM DDL from the TS side (NestJS Swagger Plugin, MikroORM) -- and the support for reflecting on classes seems much greater than that for pure type or interface. So was looking for dynamic class property as a hack...

You see the forest, I was looking for a way to water my tree.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug A bug in TypeScript Fix Available A PR has been opened for this issue
Projects
None yet
Development

Successfully merging a pull request may close this issue.