2

I would like to have array of objects indexed by numbers and and also to put all of the objects on the array under specific key

Something like this:

const myArray:ICustomArray = []
myArray.push(item)
myArray[item.key] = item;

But I am struggling to define its interface. First I expected something like this will work but it does not.

export interface ICustomArray extends Array<IItem> {
  [index: number] : IItem;
  [key: string] : IItem;

}

3 Answers 3

2

The problem with your type is that it is inconsistent with regard to the string index signature ([key: string] : IItem;). Not all keys accessed this way will be of type IItem if you inherit array. For example myArray['map'] will be the array function not IItem. This is the reason typescript forces the string index signature to be compatible with ALL statically declared members of the interface.

There is a loophole in this check though. The intersection type loophole. We can declare ICustomArray as an intersection of array and a type that has the index signature.

export type ICustomArray = Array<IItem> & {
  [key: string] : IItem;
}

let item: IItem;
const myArray: ICustomArray = [] as ICustomArray
myArray.push(item)
myArray[item.key] = item;

This will mostly have work the way you would expect:

let o = myArray['map'] // o is a function of type  <U>(callbackfn: (value: IItem, index: number, array: IItem[]) => U, thisArg?: any) => U[]
let i = myArray['key'] //IItem
declare let randomStr: string
let d = myArray[randomStr] //IItem .. but if randomStr= map, we have a runtime problem
Sign up to request clarification or add additional context in comments.

Comments

1

What you are doing is weird and Typescript team intentionally does not support such weird ideas. Hence, you have to do some weird workarounds (sorry for writing "weird" 3 times):

interface IItem {
    key: string
}

type ICustomArray = Array<IItem> & {
    [key: string]: IItem
}

const myArray: ICustomArray = [] as unknown as ICustomArray

myArray.push(item)
myArray[item.key] = item;

The weirdness is in combining array and object. Moreover, the object can have any keys. There is no much benefit from an interface with arbitrary keys.

Comments

0

I have been thinking a bit about the infamous TypeScript Dictarray, as this has come up a few times.

There is a quick and dirty fix that basically supresses the error on type creation. The reason this is interesting is that the type checking and inference both work as expected if you do this. You are effectively ignoring the conflice between all of the members of Array with your string-keyed items.

interface Dictarray extends Array<string> {
    [index: number]: string;
    // @ts-ignore: I'm creating a Dictarray!
    [key: string] : string;
}

If you use this, you can effectively carry on exactly as you anticipated when you create the type - although if you are creating a new type, you could consider not jamming them together as an option.

You can use this generic Dictarray rather than a hard-stringed version:

interface Dictarray<T> extends Array<T> {
    [index: number]: T;
    // @ts-ignore: I'm creating a Dictarray!
    [key: string]: T;
}

const dictarray = [] as Dictarray<string>;

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.