301

This is similar to #40796374 but that is around types, while I am using interfaces.

Given the code below:

interface Foo {
  name: string;
}

function go() {
  let instance: Foo | null = null;
  let mutator = () => {
   instance = {
     name: 'string'
   };  
  };

  mutator();

  if (instance == null) {
   console.log('Instance is null or undefined');
  } else {
   console.log(instance.name);
  }
}

I have an error saying 'Property 'name' does not exist on type 'never'.

I don't understand how instance could ever be a 'never'. Can anyone shed some light on this?

KushalSeth
  • 3,265
  • 1
  • 26
  • 29
Ray Booysen
  • 28,894
  • 13
  • 84
  • 111
  • 11
    It's pretty clear from your code that the `else` would indeed **never** get evaluated. The compiler is smart enough to see it. – Nitzan Tomer May 24 '17 at 02:32
  • This is an example, let me add some more code that shows it still has the issue. – Ray Booysen May 24 '17 at 04:50
  • 56
    `It's pretty clear from your code that the else would indeed never get evaluated.` It isn't obvious at all nor there is a smart compiler. There is a dumb transpiler which deviates from the established practices. In C#, for instance, one can assign `null` to any object... – Bozhidar Stoyneff Nov 14 '18 at 19:07
  • @BozhidarStoyneff, it is established but regrettable practice: https://en.wikipedia.org/wiki/Null_pointer#History – rkuksin Dec 21 '20 at 13:43
  • 2
    This becomes a serious head scratching problem when using with try catch finally variables where they need to be worked on in finally block.. – Laukik Feb 22 '22 at 14:39

21 Answers21

199

If you write the component as React.FC, and use useState(), you can write it like this:

const [arr, setArr] = useState<any[]>([])
Dave Mackey
  • 4,306
  • 21
  • 78
  • 136
blue-hope
  • 2,735
  • 1
  • 9
  • 14
  • 4
    This solved my problem, where I can't assign a type to `arr` as `arr: any`. – cst1992 Oct 21 '20 at 09:24
  • 1
    After 17 straight hours of working on an issue today, this was the root problem and the solution. Thank you – velkoon Jun 25 '21 at 08:43
  • Thank you. You saved me. I put a lot of time and effort into this error. – SandaliTP Nov 05 '21 at 04:00
  • 2
    Any reasoning behind why this solves the problem please? Is this just adding extra unnecessary Typescript or is it actually making he code more Type Safe? – Leafyshark Dec 01 '21 at 12:20
  • 2
    @blue-hope would also love to hear an explanatin. – emplo yee Dec 03 '21 at 15:50
  • 1
    In retrospect, from the point of view of the original question, the above example is just casting `instance` to `any` type to get a property called `name` and avoid `never` accessing. The method of avoiding null with ! (bang) or the method of making the compiler not infer the type as null (getFoo) as answered by other people are also valid. My solution is as same as `console.log((instance as any).name)` – blue-hope Dec 04 '21 at 12:28
  • + when assigning state as empty array, the type of state is inferred as `never` cause compiler can't infer with the literal type of array element. so, in this case(assigning empty array), we must give a type hint with `useState` generic parameter like `any[]` or `whatever[]` so that parameter can infer what type of variable can be included in array state. – blue-hope Dec 04 '21 at 12:36
  • sorry for my bad english :( . If you have any further questions, please leave them in the comments and I will be more than happy to comment. – blue-hope Dec 04 '21 at 12:37
143

I had the same error and replaced the dot notation with bracket notation to suppress it.

e.g.:

obj.name -> obj['name']
a.ak
  • 659
  • 2
  • 12
  • 26
Eric Grotke
  • 4,651
  • 3
  • 21
  • 19
136

Because you are assigning instance to null. The compiler infers that it can never be anything other than null. So it assumes that the else block should never be executed so instance is typed as never in the else block.

Now if you don't declare it as the literal value null, and get it by any other means (ex: let instance: Foo | null = getFoo();), you will see that instance will be null inside the if block and Foo inside the else block.

Never type documentation: https://www.typescriptlang.org/docs/handbook/basic-types.html#never

Edit:

The issue in the updated example is actually an open issue with the compiler. See:

https://github.com/Microsoft/TypeScript/issues/11498 https://github.com/Microsoft/TypeScript/issues/12176

Saravana
  • 37,852
  • 18
  • 100
  • 108
  • Thanks @saravana. I've updated the example with a closure which I would have hoped the compiler would pick up on. Apologies, I should have added this first. – Ray Booysen May 24 '17 at 04:52
  • Looks like an open issue with the compiler. See the following issues: https://github.com/Microsoft/TypeScript/issues/11498 https://github.com/Microsoft/TypeScript/issues/12176 – Saravana May 24 '17 at 05:30
  • There is also another issue altogether which produces a similar "wrong" error https://github.com/Microsoft/TypeScript/issues/10570 – Olga Oct 16 '18 at 18:09
  • I meet the same issue and resolve it by this: const anotherInstance = instance as Foo // access anotherInstance.name now . And of course, you should put this after the null check. – StoneLam Jul 05 '23 at 02:32
37

if you're using React useRef hook with TypeScript, instead of using any you can do the following.

const ref = useRef<HTMLInputElement>(null);
Nivethan
  • 2,339
  • 19
  • 22
  • 4
    You saved my day. For others: if your element is not input, change what's inside <> accordingly, eg. . – Betty Mar 16 '23 at 11:39
26

This seems to be similar to this issue: False "Property does not exist on type 'never'" when changing value inside callback with strictNullChecks, which is closed as a duplicate of this issue (discussion): Trade-offs in Control Flow Analysis.

That discussion is pretty long, if you can't find a good solution there you can try this:

if (instance == null) {
    console.log('Instance is null or undefined');
} else {
    console.log(instance!.name); // ok now
}
Mavaddat Javid
  • 491
  • 4
  • 19
Nitzan Tomer
  • 155,636
  • 47
  • 315
  • 299
13

In my own case when I was initiating the array. I used:

selectedActors: any = [];

So it makes it "dynamic" at first

Kazeem Quadri
  • 247
  • 3
  • 5
10

For me the following code is working:

const [disc, setDisc] = useState<any[]>([]);

What I've seen this far is the following: When you don't specify the type you are passing to the useState() it inferes it is type never. By make it use the any[] you are able to pass the data when it is requested.

Lucas Gabriel
  • 363
  • 3
  • 9
  • As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Sep 23 '21 at 04:31
9

if you're receiving the error in parameter, so keep any or any[] type of input like below

getOptionLabel={(option: any) => option!.name}
 <Autocomplete
    options={tests}
    getOptionLabel={(option: any) => option!.name}
    ....
  />
Ericgit
  • 6,089
  • 2
  • 42
  • 53
8

For typescript developer... if you use React.useRef() hook Instead of pre-assign null, declare and use variable type any.

Example:

const initialRef: any = null;
const ref = React.useRef(initialRef);
crystal
  • 185
  • 1
  • 4
5

In your component.ts file just declare the array as the following

public arrayName = ([] as any[]);

that's worked with me for Angular

Ahmed Meshaal
  • 109
  • 2
  • 4
3

It happened to me when I forgot to define a type for the variable.

Oranit Dar
  • 1,539
  • 18
  • 17
2

If you use the UseState in this way, you shouldn't have a problem

const [data, setData] = useState<any>([]);

In the case of Javascript it works directly because all data types are Any by default, and this is not the case in Typescript, so you have to let the language know :)

1

In my case (I'm using typescript) I was trying to simulate response with fake data where the data is assigned later on. My first attempt was with:

let response = {status: 200, data: []};

and later, on the assignment of the fake data it starts complaining that it is not assignable to type 'never[]'. Then I defined the response like follows and it accepted it..

let dataArr: MyClass[] = [];
let response = {status: 200, data: dataArr};

and assigning of the fake data:

response.data = fakeData;
Kiril Dobrev
  • 839
  • 1
  • 8
  • 12
1

in useState if u've never declared the type :

it assumes it is type is never so it shows this error :"Property does not exist on type 'never'"

you can type it as :

const [datalist, setDataList] = useState<Array<Object>>([]);
Rawand Deheliah
  • 1,220
  • 10
  • 11
1

you can type it as:

const [data, setData] = useState<Array>([]);

Matthabbey
  • 11
  • 2
0

In my case it was happening because I had not typed a variable.

So I created the Search interface

export interface Search {
  term: string;
  ...
}

I changed that

searchList = [];

for that and it worked

searchList: Search[];
0

I was having problems with ? and !

This piece worked for me.

if (ref != null){
    if (ref.current != null)
        ref.current.appendChild(child);
}
Ashu Sahu
  • 417
  • 6
  • 7
0

I've came across the issue with the following expression:

const node = item ? <a href={item.link}>{item.name}</a> : item.name

And the else part item.name said Property 'name' does not exist on type 'never'.

Well this is because if there is nothing inside the item object - there can't be a name property as well.

So this warning happens when you declare something basically impossible (that's why it's never)

Kia Kaha
  • 1,565
  • 1
  • 17
  • 39
0

Sometimes the value could be empty initially. In that case you might want say example

let val = data.notificationId --> here it will throw an error. So we could simply write let val = data && data.notificationId

sushmitha reddy
  • 111
  • 1
  • 5
0

My issue disappeared when I DESTRUCTURED my .MAP.

ERROR - Property 'loanId' does not exist on type 'never':

    {data?.map((el) => {
        return (
            <div key={el.loanId + el.transactionDate}>{JSON.stringify(el)}</div>
        );
    })}

NO ERROR - DESTRUCTURED:

    {data?.map((el) => {
        const { loanId, transactionDate } = el;

        return <div key={loanId + transactionDate}>{JSON.stringify(el)}</div>;
    })}
fruitloaf
  • 1,628
  • 15
  • 10
0

For typescript with vue, if you want to assign a string and null value to a variable then use like this in data field: employeeType: "" || null later you can assign null or string to employeeType variable

export default defineComponent({
 data() {
    return {
        employeeType: "" || null,
    }
.........
})
KushalSeth
  • 3,265
  • 1
  • 26
  • 29