Why Typescript Mapped tuple types shows all options in AutoCompletion/Intellisense?

305 Views Asked by At

I am very new in Typescript and I have an issue which I don't know how to resolve it :(.

Basically I want to create a list of tuple from a list of components. The first element of the tuple is the element's name (keyof MyComponents) and the second elements is its attributes.

(See code below)

playground link

 interface MyComponents  {
    Container: {
      fluid?: boolean
      className?: string
    },
    Tag: {
      text?: string
      className?: string
      hidden: boolean
    }
}

//Get the keys of the list of my components
type Element = keyof MyComponents
 
//Get the attributes depending on the element name
type PickAttributes<T extends Element> = Pick<MyComponents[T], keyof MyComponents[T]>

//Create a mapped tuple type [Element, Attributes]
// and the attributes depend on the element
export type Tuple = { 

  [Element in keyof MyComponents] : [Element, PickAttributes<Element>]

}[keyof MyComponents]

 

const attr : PickAttributes<'Tag'> = {hidden : false} //This works and the auto completion works perfectly

const tuple1 : Tuple = ["Tag", { hidden: false}] //This also works

const tuple2 : Tuple = ["Container", { hidden: false}] //Error but it's normal as the element 'Container' doesn't have the property hidden:boolean

Everything works perfectly but there is a small problem for autocompletion. When I type the first element (Container, Tag, ...), the auto completion of the second element (its attributes) shows all the possible attributes even the wrong ones.

As an example if I type 'Tag' for the first element it suggets me 'fluid' but 'fluid' is only available in 'Container' ! Intellisense shows all options

And when I choose fluid, it also knows it's incompatible...

Typescript knows it's incompatible

So my question is: How can I restrict the autocomplete to only shows valid attributes depending on the element's name ?

Any help will be appreciated ! Thanks !**

1

There are 1 best solutions below

0
On

I can't speak to your title question as to why intellisense does that, but I can give you two options to answer the question "How can I restrict the autocomplete to only shows valid attributes depending on the element's name?":

Option 1, Use objects instead of arrays to model the tuple:

export type TupleAsObject = {
                               // If desired, the keys can now be more descriptive
  [Element in keyof MyComponents] : { 0: Element, 1: PickAttributes<Element> }
}[keyof MyComponents]
const test3 : TupleAsObject = {0: "Tag", 1: { /* type will be narrowed correctly */ }}
  • Pros: It does what you want, and allows you to be more descriptive with property keys.
  • Cons: It's no longer an array, so if you are using array methods on the tuple anywhere, you'll lose that functionality.

Option 2, use a tuple constructor function:

function buildTuple<E extends Element>(el: E, attrs: PickAttributes<E>): [E,PickAttributes<E>] {
  return [el, attrs];
}
const test4: Tuple = buildTuple("Tag", { /* types will be narrowed correctly */});

// works with arrays of Tuples
const tuples: Tuple[] = [
  buildTuple("Tag", {hidden: false}),
  buildTuple("Container", {})
]
  • Pros: Does what you want and keeps the value as an array-tuple.
  • Cons: Adds a function call.

playground