microsoft / microsoft/tsdoc

RFC: "@cannotBeUndefined" for optional properties

Open
#163 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

request for comments
Dominant language
TypeScript
Stars
5k
Forks
162
Avg merge
17h 24m
Merged PRs (30d)
8

Description

Suppose an interface has optional members (possibly introduced by Partial<T>). For example:

class Person {
    public name: string = '';
    public age: number | undefined = undefined;

    public constructor(traits: IPersonTraits = {}) {
        this.update(traits);
    }

    public update(traits: IPersonTraits) {
        if (traits.name) {
            this.name = traits.name;
        }
        if (traits.hasOwnProperty("age")) {
            this.age = traits.age;
        }
    }
}

interface IPersonTraits {
    /**
     * The person's name or `""` if not known.
     */
    name?: string;

    /**
     * The person's age, or `undefined` if not known.
     */
    age?: number | undefined;
}

TypeScript currently allows undefined to be specified for both IPersonTraits.name and IPersonTraits.age. In other words, name?: string is equivalent to name?: string | undefined, and in fact gets normalized to that in the emitted .d.ts file. This is a gap in the type system. (See https://github.com/Microsoft/TypeScript/issues/13195 which proposes to improve the language to distinguish between a missing member versus an undefined value.)

This gap can lead to bugs. For example:

let person = new Person();

// CORRECT USAGE:
person.update({
    name: 'hello',
    age: undefined  // <-- overwrites "age" with undefined, as intended
});

// MISTAKE:
person.update({
    name: undefined,  // <-- assigns undefined to "name", which is not an allowed value
    age: 13
});

The compiler cannot catch this mistake, and in fact the shipping .d.ts files will contain the normalized name?: string | undefined declaration, which misleadingly implies that undefined is a meaningful value for name.

A possible solution

Suppose we introduce a TSDoc modifier tag call @cannotBeUndefined, which would be used like this:

interface IPersonTraits {
    /**
     * The person's name or `""` if not known.
     * @cannotBeUndefined
     */
    name?: string;

    /**
     * The person's age, or `undefined` if not known.
     */
    age?: number | undefined;
}

Benefits:

  • The tag will be appear in the normalized .d.ts file
  • The tag will be displayed in the VS Code tooltip, providing clear documentation
  • We could implement a TSLint rule that looks for this tag, and if found, reports an error for an expression that is possibly undefined

What do you think? Does this seem useful enough to be a standard tag?

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the proposed @cannotBeUndefined behavior and the linked TypeScript issue #13195. No file or test is identified, so first locate how TSDoc modifier tags are specified and tested, then establish acceptance criteria for standardizing the tag or closing the proposal.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
documentation
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
20/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.