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

Infer literal types for string concatenations and similar expressions #13969

Closed
donaldpipowitch opened this issue Feb 9, 2017 · 11 comments
Closed
Labels
Awaiting More Feedback This means we'd like to hear from more people who would be helped by this feature Suggestion An idea for TypeScript

Comments

@donaldpipowitch
Copy link
Contributor

TypeScript Version: 2.1.5

Expected behavior:

const foo = 'world'; // value type: 'world'
const bar = `hello ${foo}`; // value type: 'hello world'

Actual behavior:

const foo = 'world'; // value type: 'world'
const bar = `hello ${foo}`; // type: 'string'

Motivation

For constants which are created from other constants the IntelliSense is often more useful, when we see its value type instead of the "real" type.

More practical example:

const fooBreakpoint = '800px';
const fooMediaQuery = `@media(min-width: ${fooBreakpoint})`;

When fooMediaQuery is used I'd like to see @media(min-width: 800px) as its type instead of string.

@DanielRosenwasser DanielRosenwasser added Awaiting More Feedback This means we'd like to hear from more people who would be helped by this feature Suggestion An idea for TypeScript labels Feb 9, 2017
@DanielRosenwasser
Copy link
Member

DanielRosenwasser commented Feb 9, 2017

When fooMediaQuery is used I'd like to see @media(min-width: 800px) as its type instead of string.

This is interesting, mostly because it sounds like you are leveraging your editor's support of string literal types so that you can quickly glance at a type and get its content.

@donaldpipowitch
Copy link
Contributor Author

Yeah, when I work with CSS-in-JS solutions like glamor it is so great to immediately see some concrete value for different constants instead of just number or string.

import { css } from 'glamor';

const color = '#fff';
const mq = '@media(min-width: 800px)';

const myStyle = css({
  [mq]: {  // hover over `mq` → see '@media(min-width: 800px)'
    color  // hover over `color` → see '#fff'
  }
});

If infering the value type doesn't work (e.g. because of templated strings) I need to duplicate the information into a comment which of course easily gets out of date, if someone changes the values without changing the comment.

import { css } from 'glamor';

const color = '#fff';
const breakpoint = '800px';
/**
 * The media query is '@media(min-width: 800px)'.
 */
const mq = `@media(min-width: ${breakpoint})`;

const myStyle = css({
  [mq]: {  // hover over `mq` → see 'string' as its type and the comment which shows the value
    color  // hover over `color` → see '#fff'
  }
});

@DanielRosenwasser DanielRosenwasser changed the title Infer value type for easy expresssions Infer literal types for string concatenations and similar expressions Jun 16, 2017
@fjmorel
Copy link

fjmorel commented Apr 12, 2018

I'd like to be able to do something like this, building a key using string literals:

type Obj = Record<"a" | "a1" |  "b" | "b1", string>;

function getKey(useA: boolean, useOne: boolean): keyof Obj {
  // [ts] Type 'string' is not assignable to type '"a" | "a1" | "b" | "b1"'.
  return (useA ? "a" : "b") + (useOne ? "1" : "");
}

Adding a cast after the string concatenation makes the error go away, but doesn't actually check that my key generation is still valid if I change Obj.

@ethanresnick
Copy link
Contributor

ethanresnick commented Dec 8, 2018

Similar to @fjmorel, I'd love to have this distribute over a union. My use case is basically:

// i don't control the shape of x.
const x = { item1: true, item2: false, item3: true };

([1, 2, 3] as (1 | 2 | 3)[]).forEach(itemNum => {
    // would love type inference here as item1 | item2 | item3
    const itemKey = `item${itemNum}`;
});

@ascott18
Copy link

ascott18 commented Aug 21, 2019

This would be fantastic to see for indexing into objects with string literals, especially now that const assertions have been added in 3.4.

const obj= {
 sourceName: "source",
 sourceId: 1,
 targetName: "target",
 targetId: 2,
}

function getValues(prefix: "source" | "target") {
 return { name: obj[prefix + "Name"], id: obj[prefix + "Id"] }
}

@vdh
Copy link

vdh commented Jan 13, 2020

It'd be nice to be able to use either string literals or unions.

const bar = "bar";
const foobar = `foo${bar}`; // "foobar"
let union: "bar" | "baz";
const foobarOrFoobaz = `foo${union}`; // "foobar" | "foobaz"
let generic: string;
const stillGeneric = `foo${generic}`; // string

@mortoray
Copy link

This would be helpful for string enums with a common prefix.

const prefix = "mymodule_"
export enum Actions {
  actionOne: prefix + "action_one"
  actionTwo: prefix + "action_two"
}

This is currently rejected with error TS2553: Computed values are not permitted in an enum with string valued members.

@vdh
Copy link

vdh commented Jul 27, 2020

Maybe a const assertion syntax would suit this?

const bar = "bar"; // "bar"
const existingBehaviour = `foo-${bar}`; // string
const dynamic = process.env.BAR; // string | undefined
const existingBehaviourDynamic = `foo-${dynamic}`; // string
const barOrBaz = Math.round(Math.random()) ? "bar" : "baz"; // "bar" | "baz"
// A const assertion syntax…?
const fooBar = `foo-${bar}` as const; // "foo-bar"
const fooBarOrBaz = `foo-${barOrBaz}` as const; // "foo-bar" | "foo-baz"
const badDynamicVariable = `foo-${dynamic}` as const; // type error

@jcalz
Copy link
Contributor

jcalz commented Sep 3, 2020

this is not inferred yet, but now we at least have #40336 to represent string concatenation at the type level,

@bradzacher
Copy link
Contributor

Worth noting that if you add as const this should work now with template literal types!

https://www.typescriptlang.org/play?#code/MYewdgzgLgBAZiEMC8MDkB3EAnANgEzQG4YB6UmANwENcBXAUxigE8AHBgLnSz0IChQkWACNq2FDAAGACwa5cSACQBvBCAC+UmNQgwh0Iv3IxTpgHoB+IA

@jakebailey
Copy link
Member

This was fixed in #53907, which I believe has enough testing for this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Awaiting More Feedback This means we'd like to hear from more people who would be helped by this feature Suggestion An idea for TypeScript
Projects
None yet
Development

No branches or pull requests

10 participants