Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save TobiObeck/b5043b654ceb843eb77d9c21c15a4f5e to your computer and use it in GitHub Desktop.
Save TobiObeck/b5043b654ceb843eb77d9c21c15a4f5e to your computer and use it in GitHub Desktop.
Common TypeScript errors and what they mean

Array Element implicit any, type has no index signature

Element implicitly has an 'any' type because type 'MyFancyType' has no index signature.",

class MyPerson {
  public name: string;
  public subscriptions: MyFancyType[];
  
  constructor(name: string, subscriptions: MyFancyType){...}
}

// usage
someFunction(index: number, isCondition: boolean, value: any){
  const dynPropertyName = isCondition? 'price' : 'expiration'
  const person = new Person(...);
  person.subscriptions[index][dynPropertyName] = value;
                             ^^^^^^^^^^^^^^^^^ Error: implicit any ... no index signature
}

Solution:

person.subscriptions[index][dynPropertyName as keyof MyFancyType] = value;

Because TypeScript does not know that MyFancyType DOES have the properties 'price' and 'expiration'.

Alternative Solution:

interface IMySomething {
    someKey:      number;
    anotherKey:     Date;    
    [key: string]: number; // allow basically any string property with an value type of value
}

Tell TypeScript that an object of the type 'IMySomething' can have any string property and the assigned value of the string property is a number.


Property does not exist on EventTarget (Input Event / Change Event)

Property 'value' does not exist on type EventTarget in TypeScript

<input id="myInput" value="I typed something...">
<script>
document.getElementById('myInput').addEventListener('change', function(event){
  const newValue = event.target.value;
                                ^^^^^^ Error: Property does not exist on type EventTarget
})
</script>

Solution:

const newValue = (event.target as HTMLInputElement).value
// or
const newValue = (<HTMLInputElement>event.target).value

Because TypeScript does not know, that the target HtmlElement is of type input. Not every HtmlElement has a value property.


Vue.js: Event object is 'null' / 'selectedIndex' does not exist

Event Object is possibly 'null'. and Property 'selectedIndex' does not exist on type 'EventTarget'.

SomeComponent.vue inside of ...

<select @input="someChange($event)">
  <option>some option</option>  
</select>

SomeComponent.vue inside of <script lang="ts">...</script>

export default Vue.extend({
  //...
  methods: {
      someChange(event: InputEvent) {
        const index = event.target.selectedIndex;
                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^ Object is possibly 'null'.
                                   ^^^^^^^^^^^^^^ Property 'selectedIndex' does not exist on type 'EventTarget'.
      }
  }
}

Solution

onChange(event: InputEvent) {
  const index = (event.target as HTMLSelectElement).selectedIndex;
}

Type 'HTMLCollectionOf<...>' is not an array type or a string type / Property 'array method' Does not exist on type HTMLCollection<...>

Type 'HTMLCollectionOf<HTMLSomeElement>' is not an array type or a string type

Property 'push/pop/forEach/filter/map' Does not exist on type HTMLCollection<...>

<select multiple id="mySelect" onchange="myFunction(event)">
  <option value="Bread">Bread
  <option value="Butter">Butter
  <option value="Jam">Jam
</select>
const selectedCollection = (document.getElementById("mySelect") as HTMLSelectElement).selectedOptions;
const selectedArray = [...selectedCollection]
                          ^^^^^^^^^^^^^^^^^^ Type 'HTMLCollectionOf<...>' is not an array type or a string type
const lastElement = selectedCollection.pop();
                                       ^^^ Property 'pop' does not exist on type 'HTMLCollectionOf<...>'.

Solution

Use Array.from() like this:

const selectedCollection = (document.getElementById("mySelect") as HTMLSelectElement).selectedOptions;
const selectedArray = Array.from(selectedCollection);
const lastElement = selectedArray.pop();

Because HTMLCollections are array-like but not real arrays. They can be accessed with brackets myCollection[index] but TypeScript does not allow standard array methods. To convert the collection to a real array Array.from()can be used.

Argument of type '(nestedResponses: AxiosResponse[][]) => Promise<AxiosResponse[][]> | undefined' is not assignable to parameter of type '(value: AxiosResponse[][]) => AxiosResponse[][] | PromiseLike<AxiosResponse[][]>'. Type 'Promise<AxiosResponse[][]> | undefined' is not assignable to type 'AxiosResponse[][] | PromiseLike<AxiosResponse[][]>'. Type 'undefined' is not assignable to type 'AxiosResponse[][] | PromiseLike<AxiosResponse[][]>'.

import Axios from 'axios';

function getSomeDataConditionally(
    someSuccessCallback: (data?: any) => void,
    errorCallback: (error: any) => void){

    Axios.get('myendpoint')
        .then((response: any) => {

            // do some stuff

            return Axios.get('myendpoint2');
        })
        .then((response2) => {
              ^^^^^^^^^^^^^^^^ // return value of then callback is sometimes not allowed to be undefined by TypeScript
                               // note reproducible by this example
            if (response2.data.someCondition === false) {
                someSuccessCallback();
                
                //const unknownPromise = new Promise((resolve, rejected) => {
                //    resolve();
                //})
                //return unknownPromise;

                //const someArrayPromise = Axios.all([]);
                //return someArrayPromise;

                //const anyPromise: Promise<any> = Promise.resolve();
                //return anyPromise;

                // casted as a one liner
                //return <Promise<any>>Promise.resolve();
                //return (Promise.resolve() as Promise<any>);
                
                return; // this line causes the error. Replace it with one of the commented lines above
                // or use any as the return type of the then callback: (response2): any => {...}
            }

            return Axios.get('evenmoredata');
        })
        .then((evenMoreDataRes: any) => {

            // do some stuff with evenMoreDataRes, 
            // because condition was met
                        
            return Axios.get('evenmoredata2');
        })
        .then((evenMoreDataRes2: any) => {

            // do some stuff with evenMoreDataRes2, 
            // because condition was met
            
            someSuccessCallback(evenMoreDataRes2.data);
        })
        .catch((error: any) => {
            errorCallback(error);
        });
}

//called from view
getSomeDataConditionally(
    (data) => {
        if (data != null) {
            // update view with fresh data
            console.log(data);
        }
    },
    (error) => {
        // update view with error
        console.log(error);
    }    
)

Enforcing the type of indexed properties of an object?

inline for single use:

var stuff: { [s: string]: string; } = {};

or

interface for reuse

interface StringMap { 
  [s: string]: string; 
}
var stuff: StringMap = { };
stuff['a'] = ''; // ok
stuff['a'] = 4;  // error
```
npm run lint -- --fix
```

Keys and Index

export interface TextByLanguageCode {
    [key: number]: string;
}

const someText: TextByLanguageCode = {
    1031: 'Hallo Welt!',
    1033: 'Hello World'
}

const payload = Object.keys(someText).map((key: string) => {
    return {        
        name: someText[key as any],
        languagecode: Number(key)
    };
});

let payload2 = [];
for (const key in someText) {
  let value = someText[key];

  if (someText.hasOwnProperty(key)) {
      const tempPayload = {
        name: someText[key],
        languagecode: Number(key)
    };
    payload2.push(tempPayload);
  }
}
console.log(payload, payload2);

// alternatives to [key as any]
// name: (someText as { [key: string]: string })[key],
// name: someText[(key as unknown) as number]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment