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

[Feature Suggestion] File satisfies interface #52487

Closed
5 tasks done
jeengbe opened this issue Jan 29, 2023 · 1 comment
Closed
5 tasks done

[Feature Suggestion] File satisfies interface #52487

jeengbe opened this issue Jan 29, 2023 · 1 comment

Comments

@jeengbe
Copy link

jeengbe commented Jan 29, 2023

Suggestion

πŸ” Search Terms

File implements interface, file satisfies, file interface

βœ… 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 behaviour 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 way to declare the export signature of a whole file.

Consider, for instance, NextJS pages. Every page must default-export a React Component that satisfies a certain type and may export additional methods, such as getStaticProps. Currently, every export in every file must be explicitly annotated, which is repetitive and error- and inconsistency-prone.

Instead, (on a per-file-basis?; with glob src/pages/**/*?) exports for the entire file can be declared at once, which comes with the added benefit of automatic type inference for parameters, return types etc. for exported stuff.

πŸ“ƒ Motivating Example

When writing pages for NextJS, annotate page files with

/// <exports satisfy="next#NextPageExports" />

// Return type automatically checked
export default () => { ... };

// Parameters automatically inferred
export const getStaticProps = (ctx) => { ... };

πŸ’» Use Cases

Many systems (both backend and frontend) use a modularized approach for declaring essentially anything. Consider a CMS whose individual components are found under something like:

└── src/
    └── components/
        β”œβ”€β”€ Box.tsx
        β”œβ”€β”€ Textarea.tsx
        β”œβ”€β”€ FancyWhatnot.tsx
        └── index.ts

A component can be described with the following interface:

interface CMSComponent {
  ID: string;
  description?: string;
  render(...): ...;
  renderEdit(...): ...;
}

with the index file re-exporting components with

// src/components/index.ts

import * as Box from "./Box";
import * as ... from "./...";

export interface CMSComponent {
  ID: string;
  description?: string;
  render(...): ...;
  renderEdit(...): ...;
}

Box satisfies CMSComponent;
// ^^^ Property 'renderEdit' is missing in type '{ ID: string; render: (...) => ... }' but required ...
... satisfies CMSComponent;

export {
  Box,
  ...,
};

The X satisfies CMSComponent is currently the best way to check the exported types. The problem of this is that if a component were, for instance, missing props, the error would be thrown in the wrong place. It would be the file that exported properties incorrectly, not the index file that used a file incorrectly.

With this proposal, an incorrect file would throw an error something like the following:

// src/components/Box.tsx

/// <exports satisfy=".#CMSComponent" />
//  ^^^ This file does not satisfy its export types. Property 'renderEdit' is missing in type '{ ID: string; render: (...) => ... }' but required ...

export const ID =  ...;

export const render = (...) => { ... };

Consider a server-side project, in which such an index file may not even exist, with files being imported dynamically. Here, the user would need to make sure that all files would be set up correctly to not run into runtime errors.

With some way to declare to declare glob-based export patterns, this could be prevented entirely:

// src/components/index.tsx

/// <exports satisfy="#CMSComponent" in="./**/*" />

export interface CMSComponent {
  ID: string;
  description?: string;
  render(...): JSX.Element;
  renderEdit(...): ...;
}
@MartinJohns
Copy link
Contributor

Sounds like a duplicate of #38511.

@jeengbe jeengbe closed this as not planned Won't fix, can't repro, duplicate, stale Jan 29, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants