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

Quick fix for 'unions can't be used in index signatures, use a mapped object type instead' #24220

Closed
DanielRosenwasser opened this Issue May 17, 2018 · 15 comments

Comments

@DanielRosenwasser
Copy link
Member

DanielRosenwasser commented May 17, 2018

The following code:

type K = "foo" | "bar";

interface SomeType {
    [prop: K]: any;
}

Gives this error message:

An index signature parameter type cannot be a union type. Consider using a mapped object type instead.

Nobody knows what mapped object types are, so let's give them a quick fix that

  • Switches the index signature to a mapped type
  • Moves other members to a separate object type that gets combined with an intersection type
  • Changes the containing object type to a type alias if the containing object type is an interface
  • Intersects with all the extends clauses if the containing object type is an interface and has any extends clauses
@dannycochran

This comment has been minimized.

Copy link

dannycochran commented May 18, 2018

Nobody knows what mapped object types are, so let's give them a quick fix that

+1, just came here because I was expecting 2.9 to support unions as index signatures per your example code. I think this has been a long desired feature: #5683, #16760, etc..

@mattbasta

This comment has been minimized.

Copy link

mattbasta commented May 18, 2018

You can do this:

type Foo = 'a' | 'b';
type Bar = {[key in Foo]: any};

Though Bar has no index signature (i.e., you can't then do (obj as Bar)[value as Foo]).

Edit: Though if you could make the caveat a non-issue, I'd be eternally grateful!

@Kingwl

This comment has been minimized.

Copy link
Collaborator

Kingwl commented May 20, 2018

i'd like to work on this 😆

@Kingwl

This comment has been minimized.

Copy link
Collaborator

Kingwl commented May 21, 2018

Moves other members to a separate object type that gets combined with an intersection type

what should we do if containing object type is an class?
I can only imagine that it is an interface

so what should follow code do after quickfix?

type K = "1" | "2"

class SomeType {
    a = 1;
    [prop: K]: any;
}
@mhegazy

This comment has been minimized.

Copy link
Contributor

mhegazy commented May 21, 2018

so what should follow code do after quickfix?

I would say this should not be fixable..

@dannycochran

This comment has been minimized.

Copy link

dannycochran commented Jul 18, 2018

@mhegazy I'm using 3.0.0-rc and still getting the same error as originally posted. Is this expected?

@mhegazy

This comment has been minimized.

Copy link
Contributor

mhegazy commented Jul 18, 2018

I'm using 3.0.0-rc and still getting the same error as originally posted. Is this expected?

yes. the error is correct. this issue was tracking adding a quick fix for it, that is the light pulp next to the error message.

@ThaJay

This comment has been minimized.

Copy link

ThaJay commented Aug 15, 2018

no code actions available with 2.9.1 and vscode

@andy-ms

This comment has been minimized.

Copy link
Member

andy-ms commented Aug 15, 2018

@ThaJay We won't backport this feature, try setting up a newer version.

@ThaJay

This comment has been minimized.

Copy link

ThaJay commented Aug 15, 2018

Obviously. I'm sorry for not checking the timeline first, just assumed it would be new enough. New to ts. Will check with version 3.

@maicWorkGithub

This comment has been minimized.

Copy link

maicWorkGithub commented Sep 20, 2018

how to describe this:

function createRequestTypes(base){
  return ['REQUEST', 'SUCCESS', 'FAILURE'].reduce((acc, type) => {
    acc[type] = `${base}_${type}`
    return acc
  }, {})
}

const user = createRequestTypes('USER')
console.log(user.REQUEST) // error
// just string? like:
interface IRequestType: {[key: string]: string}

I tried below, all failed:

type requestStatus = 'REQUEST' | 'SUCCESS' | 'FAILURE'
type requestTypes = {
  [key in requestStatus]: string
}
// or
interface IRequestTypes {[key: keyType]: string}
// or even
type requestTypes = {
  FAILURE: string,
  SUCCESS: string,
  REQUEST: string
}
@ihorskyi

This comment has been minimized.

Copy link

ihorskyi commented Sep 23, 2018

@maicWorkGithub here you go:

const user = createRequestTypes('USER')
console.log(user.REQUEST) 

function createRequestTypes(base:string):requestTypes {
  const result : requestTypes    = {}
  const arr    : requestStatus[] = ['REQUEST', 'SUCCESS', 'FAILURE']  
  
  return arr.reduce((acc, type) => {
    acc[type] = `${base}_${type}`
    return acc
  }, result)
}


type requestStatus = 'REQUEST' | 'SUCCESS' | 'FAILURE'
type requestTypes = { [key in requestStatus]?: string }
@maicWorkGithub

This comment has been minimized.

Copy link

maicWorkGithub commented Sep 27, 2018

@ihorskyi Thanks!!

@mvasin

This comment has been minimized.

Copy link

mvasin commented Dec 21, 2018

Just curious why type works, but interface doesn't. Can someone explain, please? What's the reason for such a limitation (or a feature?) of interface.

type Foo = 'a' | 'b';
type Bar = {[key in Foo]: any}; // ok
interface Baz {[key in Foo]: any} // =>

// A computed property name in an interface must refer to an expression whose type is a literal type or a 'unique symbol' type.ts(1169)
// A computed property name must be of type 'string', 'number', 'symbol', or 'any'.ts(2464)
// 'Foo' only refers to a type, but is being used as a value here.ts(2693)
@dgreene1

This comment has been minimized.

Copy link

dgreene1 commented Jan 4, 2019

This was an amazing auto-fix to discover. Thank you for implementing it! :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.