typescriptundefined

Can TypeScript specify keys that are allowed to not exist, but cannot be assigned to `undefined`?


Summary

I want to disallow any explicit undefined value while still allowing implicit undefined values.

Background

In JavaScript, we have this behavior where accessing a key that does not exist yields undefined, while accessing a key that does exist can also yield undefined.

For example,

  1. [1, 2, undefined, 4][2] -> undefined
  2. [1, 2, 3, 4][99] -> undefined
  3. {a: 1, b: undefined}.b -> undefined
  4. {a: 1, b: 2}.foo -> undefined.

Examples 1 and 3 are accessing values that are explicitly undefined, whereas examples 2 and 4 are accessing implicitly undefined values.

Question

What I want to do is specify a key that may or may not exist (optional), but if it DOES exist, it CANNOT be undefined. Is this possible?

More Details

// - mustExistAndCouldBeUndefined MUST exist in this type, but it could be undefined.
// - couldExistAndCouldBeUndefined COULD exist in this type. If it does, it could
//   be undefined. If it doesn't, it's undefined.
// - couldExistButCannotBeUndefinedIfItDoes COULD exist in this type. If it does,
//   it CANNOT be undefined. If it doesn't, it's undefined.
type Example = {
    mustExistAndCouldBeUndefined: number | undefined;
    couldExistAndCouldBeUndefined?: number;
    ??? couldExistButCannotBeUndefinedIfItDoes ???
};

// I want this to be legal, since couldExistButCannotBeUndefinedIfItDoes is allowed
// to not exist (implicitly undefined).
const a: Example = {
    mustExistAndCouldBeUndefined: undefined,
};

// I want this to be ILLEGAL since couldExistButCannotBeUndefinedIfItDoes is not
// allowed to both exist and be undefined (explicitly undefined).
const b: Example = {
    mustExistAndCouldBeUndefined: undefined,
    couldExistButCannotBeUndefinedIfItDoes: undefined,
};

Is it possible to create something that behaves like couldExistButCannotBeUndefinedIfItDoes in the code block above?


Solution

  • You can enable the Typescript rule exactOptionalPropertyTypes (Docs) in the tsconfig.json to allow the ts to differentiate between undefined and optional.

    Without the rule:

    type TypeForTest = {
      optionalAndUndefined?: string;
    }
    

    This will make the optionalAndUndefined undefined if it does not exist and if it's undefined by value.

    So this will be allowed:

    const x:TypeForTest = {}
    // OR
    const x:TypeForTest = {
      optionalAndUndefined: undefined;
    }
    

    But after the rule is enabled, it will allow only:

    const x:TypeForTest = {}
    

    but this will not be allowed:

    const x:TypeForTest = {
      optionalAndUndefined: undefined;
    }
    

    On the other hand, if you have the following type:

    type TypeForTest = {
      optionalAndUndefined: string | undefined;
    }
    

    this will now be allowed:

    const x:TypeForTest = {}
    

    but this is ok:

    const x:TypeForTest = {
       optionalAndUndefined: undefined;
    }