简体   繁体   中英

Declare/change the current type of an object in Typescript

Typescript is great about type checking, but sometimes I know better than it does.

I'd like to be able to say "From this scope on, pretend variable x is of type y"

const array = ["foo", {bar: "quz"}];
array.forEach((it) => {
  if (it !== "foo") {
    declare it: {bar: "quz"}; // not real code, what I'd like to do
    it.bar; // Error: Property 'bar' does not exist on type 'string | { bar: string; }'
  } 
});

  • I know I could write a function to do isBarQuz(checkMe: any): checkMe is {bar: "quz"} ;
  • I also know I could define my array types.
  • I also know that in this example, my array could be const array = [...] as const
  • I could also define to a new variable, then change all my code to use that one instead

I don't want to do any of these things. I just want to gloss over a bit of type checking and get on with my work. How can I do this?

When your code is correct but Typescript's type-checking algorithm doesn't know that it's correct, you have two options: either rewrite the code in a way such that Typescript gets it right, or tell Typescript not to check that part of the code.

The former is usually preferred, because if you change the code later, you'd usually like Typescript to be able to check that you didn't make it stop working. From the wording of your question ( "I just want to gloss over a bit of type checking" ), it sounds like you might prefer the latter here.

Getting it to type-check

To get this code to type-check, you need is to specify that the contents of array are either of type 'foo' , or they are of type {bar: 'quz'} . Then when you test that it is not 'foo' , control-flow type narrowing occurs, and the type of it is narrowed to {bar: 'quz'} inside the if block.

const array: ('foo' | {bar: 'quz'})[] = ['foo', {bar: 'quz'}];

Of course, writing such a type annotation means repeating yourself; the annotation doesn't say anything that can't be inferred from the array literal itself if you write as const :

const array = ['foo', {bar: 'quz'}] as const;

Either way, you don't need anything special inside the if block to explicitly declare that it has a narrower type than it did before the if block; Typescript narrows its type automatically.

If you want all of the code to be type-checked, then you must let Typescript know that those two types are the only options somehow, otherwise ruling out one option doesn't imply that it's the other option. as const is the simplest way to do so.

Telling Typescript not to check it

If you want to "gloss over" type-checking in the sense of asking Typescript not to type-check part of your code, the simplest way is to use any :

array.forEach((it: any) => {
  if (it !== "foo") {
    it.bar; // no error
  }
});

// or:
array.forEach((it) => {
  if (it !== "foo") {
    (it as any).bar; // no error
  }
});

This is the intended purpose of any : it disables type-checking of the variable it , either for the whole function or for a single use.

The other ways to disable type-checking for part of your code are to:

  • Use a type assertion with the stricter type (eg (it as {bar: 'quz'}).bar ). Typescript will do a very weak check to make sure your assertion isn't impossible, but otherwise it trusts that you have asserted the right type.
  • A user-defined type guard . This is unchecked in the sense that Typescript won't make sure your function returns false when the argument isn't of the right type.
  • An assertion function as in @jcalz's comment. This is unchecked in the sense that Typescript won't make sure your function throws an exception when the argument isn't of the right type.

These alternative options give you more safety than any , but take more code to write, so they may be unsuitable if brevity is important for you.

const arr = ["foo", {bar: "quz"}];
    arr.forEach((it) => {
      if (it !== "foo") {
       console.log((it as any).bar);// gives quz

    });

Hope it helps

This doesn't work for const variables or non union types, but it's quick and easy enough I can fill out the broad strokes on something before getting caught up in every feasible permutation.

You can reassign a variable to itself with a different type.

interface IBarQuz { bar: "quz" };
const barquz: IBarQuz = { bar: "quz" };

const array = ["foo", barquz];
array.forEach((it) => {
  if (it !== "foo") {
    it = it as IBarQuz
    // alternate: it = it as typeof barquz;
    it.bar;
  } 
});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM