镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
Skip to content

Control flow analysis for destructured rest element of discriminated union #46680

Description

@lazytype

Suggestion

🔍 Search Terms

rest spread element destructure destructuring discriminated union narrow refine

✅ 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

A natural extension of #46266 would be to support the following

type Action =
    | { kind: 'A', payload: number }
    | { kind: 'B', payload: string };

function example({ kind, ...rest }: Action) {
    if (kind === 'A') {
        rest.payload.toFixed();
    }
    if (kind === 'B') {
        rest.payload.toUpperCase();
    }
}

📃 Motivating Example

💻 Use Cases

Activity

  1. ahejlsberg commented on Nov 4, 2021

    @ahejlsberg
    Member

    Yeah, might be nice to support. Though it's an expensive way to do it as you're incurring an extra object allocation.

  2. lazytype commented on Nov 4, 2021

    @lazytype
    Author

    Yep, but par for the course for contexts like React where you might extract some props and forward the rest

    import React from 'react'
    
    type Props =
      | ({ as: "div" } & React.ComponentPropsWithRef<"div">)
      | ({ as: "span" } & React.ComponentPropsWithRef<"span">)
    
    function Component({ as, ...rest }: Props) {
      if (as === 'div') {
        return <div {...rest} />
      }
      if (as === 'span') {
        return <span {...rest} />
      }
    
      // I think something like https://github.com/microsoft/TypeScript/issues/30581 would let us just do:
      // return <as {...rest} />
    }
  3. andrewbranch commented on Nov 4, 2021

    @andrewbranch
    Member

    Can confirm this looks like something React devs do

  4. mhofman commented on Jan 29, 2022

    @mhofman

    FYI, this is not only a React pattern, we would like to be able to write something like the following, based on #47190:

    type Message = {
     method: string,
     args: unknown[],
     result?: string,
    };
    type KernelDeliveryMessage = [tag: 'message', target: string, msg: Message];
    type KernelDeliveryNotify = [tag: 'notify', resolutions: string[] ];
    
    type KernelDeliveryObject = KernelDeliveryMessage | KernelDeliveryNotify;
    
    declare function translateMessage(target: string, msg: Message): any;
    declare function translateNotify(resolutions: string[]): any;
    
    type KernelDeliveryToVatDelivery = (...args: KernelDeliveryObject) => any;
    
    const kernelDeliveryToVatDelivery:KernelDeliveryToVatDelivery = (type,...args) => {
        switch (type) {
          case 'message':
            return translateMessage(...args);
          case 'notify':
            return translateNotify(...args);
          default:
            throw new Error(`unknown kernelDelivery.type ${type}`);
        }
    }
    Output
    "use strict";
    const kernelDeliveryToVatDelivery = (type, ...args) => {
        switch (type) {
            case 'message':
                return translateMessage(...args);
            case 'notify':
                return translateNotify(...args);
            default:
                throw new Error(`unknown kernelDelivery.type ${type}`);
        }
    };
    Compiler Options
    {
      "compilerOptions": {
        "strict": true,
        "noImplicitAny": true,
        "strictNullChecks": true,
        "strictFunctionTypes": true,
        "strictPropertyInitialization": true,
        "strictBindCallApply": true,
        "noImplicitThis": true,
        "noImplicitReturns": true,
        "alwaysStrict": true,
        "esModuleInterop": true,
        "declaration": true,
        "experimentalDecorators": true,
        "emitDecoratorMetadata": true,
        "target": "ES2017",
        "module": "ESNext",
        "moduleResolution": "node"
      }
    }

    Playground Link: Provided

  5. added a commit that references this issue on Oct 30, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    In DiscussionNot yet reached consensusSuggestionAn idea for TypeScript

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions