Repository: TypeScript
Issue: Need some way to propagate optional fields from `satisfies` to type it is used on
Labels: Suggestion,Awaiting More Feedback

# Suggestion

Give some way to do `satisfies`-like check checking for type with optional properties and modyfy left-handside type of the check to make these prperties available for access with type `never`.

## Solution 1

Use `extends` keyword similar to `satisfies`, but with propagating optional fields with type `never` to computed type it is used on:

```ts
import type { Equal, Expect } from '@type-challenges/utils'

const obj1 = { a: 1, c: 3 } as const   satisfies   Partial<Record<"a" | "b" | "c", number>>;
const obj2 = { a: 1, c: 3 } as const    extends    Partial<Record<"a" | "b" | "c", number>>;
//                                    ^^^^^^^^^^^

type cases = [
  Expect<Equal<typeof obj1, {
    readonly a: 1;
    readonly c: 3;
  }>>,
  Expect<Equal<typeof obj2, {
    readonly a: 1,
    readonly b: never, // <<<<<<<<
    readonly c: 3,
  }>>,
]
```

## Solution 2

Introduce new special type `missing` that is equvalent of optional `never` but without appearing in `keyof` and make `satisfies` set this type to missing optional properties and propagete it on type it is used on.

```ts
import type { Equal, Expect } from '@type-challenges/utils'

const obj = { a: 1, c: 3 } as const satisfies Partial<Record<"a" | "b" | "c", number>>;

type cases = [
  Expect<Equal<typeof obj, {
    readonly a: 1;
    readonly b: missing,
    readonly c: 3;
  }>>,
  Expect<Equal<keyof typeof obj, "a" | "c">>,
  Expect<Equal<keyof { x: number; y: missing }, "x">>,
  Expect<Equal<keyof { x: number; y: never }, "x" | "y">>,
]

const smth1: { x: number, y: missing } = { x: 0 } // Fine
const smth2: { x: number, y?: never } = { x: 0 } // Fine
const smth3: { x: number, y: never } = { x: 0 } // Currently an error
```

By the way, this type may be useful in many other cases.

## 🔍 Search Terms

satisfies Partial never

## ✅ Viability Checklist

My suggestion meets these guidelines:

* [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 feature would agree with the rest of [TypeScript's Design Goals](https://github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals).

## ⭐ Suggestion

When I use `satisfies` I actually expect that I can use on an object all kinds of property access that were available on type I'm using for the assertion. However I came up to a situation where I can't. At first I thought it's a bug, but then I realized that "fixing" it will break `keyof` functionality, so I desided that it would be incorrect to modify `satisfies` behaviour, so I suggest to use another keyword for this functionality.

[Playground](https://www.typescriptlang.org/play?ts=5.2.0-beta#code/C4TwDgpgBAyg9gWwgaQiAzlAvFARAQ1ygB88AjI03AY1wCg7RJYFgALbKABXwCdgAlvgA2AHgBKEanF4ATUfCSoMAGigA7AK4IyEXgD59DaevTAocMgCsAjJwDeUfAC4oNtdVcBmKAF8nmCZmUOj4gugAZgIQmDCsbADcxnCm5pZWAEwOTq7uUJ5QPv74mPZ0UFC8EPiyKcIgOW4q5ZXVter1UGSu6hAAbnrNFVU1dQ0FXs3+oeFRMSzsSXRB5gDWaOiuiigbANoAupy7BLhquBRntPsMEZrq1IIpUBEAFAAeW-EAlFBlFUFwYQQAB0wjgAHMXusMMCEPgwFC0Nh9FA3rtofsvl8ElAAPS4qAAMQEvTovhuL3SNmxeIJxNJAKBoIhiJhcIR0ORFmsNnRaExNPxUAAorxeDIKekMoK6SSIMsUuhASCwZDoehYfDWVypXyQAKcUL6fLGOBoMg7DhoXAIlAmBAbdzbLSiXLXCcSHhaKbmMgsla0I77Y6pS6APJwMCbPCET3nShe+hAA)

```ts
type SomeKeys = "a" | "b" | "c"

type Smth = Partial<Record<SomeKeys, number>>

const obj1 = { a: 1, c: 3 } as const satisfies Smth;

const obj2 = { a: 1, c: 3 } as {
  readonly a: 1,
  readonly b: never,
  readonly c: 3,
} satisfies Smth;

const keys: SomeKeys[] = ["a", "b", "c"]

function f(x: Smth) {
  console.log(keys.map(key => x[key])); // Fine
}

f(obj1); // Fine
console.log(keys.map(key => obj1[key])); // Error

f(obj2); // Fine
console.log(keys.map(key => obj2[key])); // Fine

type K1 = keyof typeof obj1 // Fine: "a" | "c"
type K2 = keyof typeof obj2 // Oops: "a" | "b" | "c"
```

`satisfies Smth` on `obj1` checkes that key `b` can be used to index it, anyway as type of `obj1` doesn't include key `b` the code doesn'ty compile with

> Element implicitly has an 'any' type because expression of type 'SomeKeys' can't be used to index type '{ readonly a: 1; readonly c: 3; }'.
  Property 'b' does not exist on type '{ readonly a: 1; readonly c: 3; }'.(7053)

## 📃 Motivating Example

In this example it's possible to use type normally without `satisfies` but I didn't want to overcomplicate an example. I hope it's idea is clear.

[Playground](https://www.typescriptlang.org/play?#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wChSYBPMJOABRQHMkBnOAXjgCIALXJLnAA+3FqihoegkVwB2SAO4tp3MDkzAANgPJoIslvDBNWALnomWAbQC6HOFd78uAGlHjJr7vKVcb5YFkYJChMdFoAVTEoOABvUjg4WRQQJHNDKEDGAG4EuGAWZEYC4KIAE3MAIwgIbRRZXMS0eoBlDx4qmrqG0gBfXX1DOGbZAHkaWQZme1i4AHo5uAA5CHhNEqQyuGNmNhQiOBQANxQtFErtOExoOCQjkMp9JDy1CA1tcwBXaI4APjhviEAHQFIobcouPJifaSL4-dj-QFQIEjNowniQ3qHNh6AzwFgoGAFDSsCywYAoTQAHmQeigZSpU1YbgAFEjzFEQgBKP5waq1VCyX6-XKkTCfWQYYD6ZbHYCMQnS2Qs2ZoT5QIhBTkxXrmVXqzUwbUcn69HnxRJEGDq2RwFl5RJU5JHX4OxJxHasIEgFBgFme3kjcZISYmKyemwAfiBLLVGpDRuiPPYKaulLEwhE9vdObgVJQcB4REw7FiAAM5gASWIhvRlJARBAASQAwrhIPIgv6TFzemXer9Yp7elS5ihfly3Yle27R87XdO+qQgA)

```ts
import React from 'react'

type Pages = "home" | "search" | "news" | "profile"

const pages: Pages[] = ["home", "search", "news"]

interface User {
  name: string;
  isRegistered: boolean;
  canSearch: boolean;
}

const canOpenPage = { // Not listed pages are available for everyone
  profile: user => user.isRegistered,
  search: user => user.canSearch,
} as const satisfies Partial<Record<Pages, (user: User) => boolean>>;

function Navigation({ currentUser }: { currentUser: User }) {
  return (
    <nav>
      {pages.map(page => canOpenPage[page]?.(currentUser) === false || (
        <a href={`/${encodeURIComponent(page)}`}>{page}</a>)
      )}
    </nav>
  )
}
```
 Should not have error

> Element implicitly has an 'any' type because expression of type 'Pages' can't be used to index type '{ readonly profile: (user: User) => boolean; readonly search: (user: User) => boolean; }'.
  Property 'home' does not exist on type '{ readonly profile: (user: User) => boolean; readonly search: (user: User) => boolean; }'.

## 💻 Use Cases

Usages with `Partial<Record<...>>` and keys enumeration seem the most likely for me.