Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Proposal] Non-null assertions at block-scope level #49079

Closed
5 tasks done
DaviDevMod opened this issue May 12, 2022 · 5 comments
Closed
5 tasks done

[Proposal] Non-null assertions at block-scope level #49079

DaviDevMod opened this issue May 12, 2022 · 5 comments
Labels
Duplicate An existing issue was already created

Comments

@DaviDevMod
Copy link

DaviDevMod commented May 12, 2022

Suggestion

This proposal asks for an implementation of non-null assertions at block-scope level, that may remove the need for unnecessary non-null assertion all over the block-scope (or if you are willing to mess with the runtime JavaScript, would remove the need for unnecessary checks at runtime).

Related to this proposal: #10421
Special case of this one: #23250 (which has been dismissed for the particular use case exposed)

In my particular use case, I have a function receiving an object with optional properties and I know for sure that if one of them exists, all of them exist; therefore if one of them doesn't exist, none of them exist and in that case the function should return straight away, before doing anything.

So I have two possibilities:

  1. Check for the existence of only one of the properties and return if it doesn't exists (like I would do in JavaScript). Then add non-null assertions all over the function to tell TypeScript that the other properties exist too.
  2. Write an unnecessary (and, in my case, long) condition checking for the existence of every property.

I can see also a third possibility that is using an array instead of an interface and check the length, but there would be side effects that are undesired in my case (like having to use integers to access the members, and in doing so, saying goodbye to readability).

🔍 Search Terms

assertion block scope level

✅ Viability Checklist

My suggestion meets these guidelines:

  • This wouldn't be a breaking change in existing TypeScript/JavaScript code
  • This wouldn't change the runtime behavior of existing JavaScript code
  • This could be implemented without emitting different JS based on the types of the expressions
  • This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.)
  • This feature would agree with the rest of TypeScript's Design Goals.

⭐ Suggestion

Provide TypeScript users with the expressive power to let the compiler know that a variable is non-null within the scope of a block of code.

📃 Motivating Example

// Trivially, if there is only one person in a group of people,
// that person is both the youngest and the oldest in the group.
// One can argue that a person is not a group, but that's not the point.
 

type Person = number | boolean | string;

interface GroupOfPeople {
  oldest?: Person;
  youngest?: Person;
  tallest?: Person;
  shortest?: Person;
  youGettiest?: true;
}

function doThings(group: GroupOfPeople) {
  const { oldest, youngest, tallest, shortest, youGettiest } = group;

  // Case 1.
  if (!oldest) return;
  youngest!.play();
  tallest!.lookDown();
  shortest!.goUnnoticed();
  youGettiest!;

  // Case 2.
  if (!oldest || !youngest || !tallest || !shortest || !youGettiest) return;

  // Possible solution.
  if(!oldest) return;
  // This won't go in the compiled JavaScript.
  assert youngest, tallest, shortest, youGettiest;
}

💻 Use Cases

Generalizing the example above, the proposed feature would be useful any time a variable is known to be non-null in a certain context.

It would be just an extension of the already existing non-null assertion, which I guess got implement just to avoid unnecessary conditions (that would have shown up in the compiled JavaScript).

@demurgos
Copy link

demurgos commented May 14, 2022

This kind of issue can generally be fixed by having more precise types.

interface Person {
  play(): void;
  lookDown(): void;
  goUnnoticed(): void;
}

type GroupOfPeople = EmptyGroupOfPeople | NonEmptyGroupOfPeople;

// You could also use mapped types to derive `EmptyGroupOfPeople` automatically from the non-empty version.

interface EmptyGroupOfPeople {
  oldest: undefined;
  youngest: undefined;
  tallest: undefined;
  shortest: undefined;
  youGettiest: undefined;
}

interface NonEmptyGroupOfPeople {
  oldest: Person;
  youngest: Person;
  tallest: Person;
  shortest: Person;
  youGettiest: true;
}

function doThings(group: GroupOfPeople) {
  const { oldest, youngest, tallest, shortest, youGettiest } = group;

  // Case 1.
  if (!oldest) return;
  youngest.play();
  tallest.lookDown();
  shortest.goUnnoticed();
}

@DaviDevMod
Copy link
Author

DaviDevMod commented May 14, 2022

@demurgos right, one could define the relationships between the properties ahead of time, sorry for not having taken into account even this possibility.

But still, I believe that the particular case in which the relationship between two properties is of existence (being either null/undefined or anything else) is common enough to be worth the feature.

The more precise typing you shown would also remove the need for non-null assertions in general, and yet they are a commonly used handy feature.

@mcFrax
Copy link

mcFrax commented May 24, 2022

So, assert in this context would mean

compiler, please assume that this is true from now on, even if you can prove it

as opposed to usual meaning of assert being either

throw runtime error if this is false

or

fail compilation if you can't prove this to be true

?

That sounds extremely dangerous. It's going to confuse people and cause errors that typescript is supposed to prevent in the first place.

I'm aware that "assertion" has this other meaning in Typescript environment, but while using this name in documentation is a bit confusing at worst, it isn't a false friend that it would be in code.

@andrewbranch
Copy link
Member

Duplicate of #10421

@andrewbranch andrewbranch marked this as a duplicate of #10421 May 24, 2022
@andrewbranch andrewbranch added the Duplicate An existing issue was already created label May 24, 2022
@typescript-bot
Copy link
Collaborator

This issue has been marked as a 'Duplicate' and has seen no recent activity. It has been automatically closed for house-keeping purposes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Duplicate An existing issue was already created
Projects
None yet
Development

No branches or pull requests

5 participants