microsoft / microsoft/TypeScript

Proposal: repurpose "is" operator for a narrowing version of "satisfies"

Open
#62,146 5 comments 4 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Awaiting More Feedback Suggestion
Dominant language
Go
Stars
111k
Forks
14.3k
Avg merge
2d 4h
Merged PRs (30d)
132

Description

### 🔍 Search Terms

satisfies with narrowing
narrowing literals
satisfias, satisfiesas, sassafras

### ✅ Viability Checklist

- [x] This wouldn't be a breaking change in existing TypeScript/JavaScript code
- [x] This wouldn't change the runtime behavior of existing JavaScript code
- [x] This could be implemented without emitting different JS based on the types of the expressions
- [x] This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.)
- [x] This isn't a request to add a new utility type: https://github.com/microsoft/TypeScript/wiki/No-New-Utility-Types
- [x] This feature would agree with the rest of our Design Goals: https://github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals

### ⭐ Suggestion

A narrowing version of the `satisfies` operator

```typescript
type Animal = "cat" | "dog";
const animal = "cat" is Animal;
/// ^? = Animal
```

### 📃 Motivating Example

Consider an API where the return type is influenced by the input type. For example, this simplification of [Tanstack Form](https://tanstack.com/form/latest/docs/overview) (I'm not a contributor to it, but I'm developing something similar).

```typescript
function useForm(defaultValues: T): T {
return /*something*/;
}

const values = useForm({
animal: "cat"
});
```

The type inferred from animal here is `string`.

This works fine if animal is a text input in our form, but what if we want it to be an enumeration, e.g. ``, and also retain some compile-time typechecking? e.g. we want to prevent this

```typescript
values.animal = "red"
```

`satifies` will error if the initial value is not valid, but it doesn't narrow the type:

```typescript
// #1
const values = useForm({
animal: "cat" satisfies Animal
});
// typeof values["animal"] = string
```

`as` achieves the equivalent of narrowing here, but it masks some errors:

```typescript
// #2
const values = useForm({
animal: "red" as Animal // no error
});
```

I propose to repurpose the existing `is` keyword. Could use another keyword; bikeshed it later.

```typescript
const values = useForm({
animal: "cat" is Animal
});
```

This would produce a compiler error if the left hand side was not an Animal (like satifies), but additionally narrow the type to Animal.

### 💻 Use Cases

**What shortcomings exist with current approaches?**

A more natural flow of the code is disrupted; the field can't be declared inline.

**What workarounds are you using in the meantime?**

This one is okay, but somewhat disrupts the flow of reading top to bottom.

```typescript
// #3
const initialAnimal: Animal = "cat";
const values = useForm({
animal: initialAnimal,
plainString: "a",
plainNumber: 1
});
```

This one requires declaring the entire type separately. Also there may be many more type args than just the data shape. This is the case in Tanstack Form. You'd need `useForm`;

```typescript
// #4
type FormValues = {
animal: Animal
}
const values = useForm({
animal: "cat"
});
```

This is probably what I find myself reaching for, but as with `#4` requires declaring the entire type

```typescript
// #5
type FormValues = {
animal: Animal
plainString: string
plainNumber: number
}
const initialValues: FormValues = {
animal: "cat",
plainString: "",
plainNumber: 0
}
const values = useForm(initialValues);
```

Contributor guide

Open the contributing guide

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

No implementation files, tests, or entry points are named. Start by reviewing the existing `satisfies` operator and type-assertion behavior described in the examples, then determine how a narrowing form of `is` would fit the compiler's type-system design. Done means the proposal has an accepted design and a defined implementation and test scope.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
compilers
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
30/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.