Refactor: Generate narrower type based on usage.
See original GitHub issueSuggestion
🔍 Search Terms
- refactor
- type
- narrow
- generate
- interface
- decouple
✅ 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 new refactor feature which creates a minimal interface based on usage for an argument.
📃 Motivating Example
Imagine we have created an interface, User.
interface User {
id: string,
created_at: Date,
updated_at: Date
name: string
email: string
}
When we started with our project, we created some super fancy rendering table which shows a list of users by name, and when they were last_updated, and when they were created.
const renderTable = (data: User[])=> {
return (
<table>
<thead> <th key={i.id}><td>name</td><td>created at</td><td>updated at</td></th></thead>
<tbody>
{data.map(i => <tr key={i.id}><td>{i.name}</td><td>{i.created_at}</td><td>{i.updated_at}</td></tr>)}
</tbody>
</table>
)
}
Then we decide to add books to our app.
interface Book {
id: string
name: string
created_at: Date
updated_at: Date
last_checkout: {date: Date, user: User}
}
We want the same functionality for rendering books as we have for users. Luckily, renderTable
is pretty re-usable. So we just add our interface to types for data:
const renderTable = (data: (User|Book)[])=> {
// ...
Then, we add Libraries, and repeat
const renderTable = (data: (User|Book|Library)[])=> {
// ...
We could keep adding to this list, but in order to avoid infinite joins, we might want to refactor this, so that the data type only specifies what it actually needs. So we create a generic interface, maybe called TableRenderable, which has all the properties we need to make the data renderable.
interface TableRenderable {
id: string
name: string
created_at: Date
updated_at: Date
}
const renderTable = (data: TableRenderable[]) => {
Nice, now we have a minimal interface that this table needs to be able to render the data.
That was easy enough, but imagine if table weren’t so simple, maybe it called half a dozen other functions, which all use the User interface, and had a few more than 3 properties.
In order to refactor, you would need to first go into each function, and create the minimal interface for each function, then on the renderTable function, create the minimal interface TableRenderable
. This still isn’t that bad, but it is a bit time consuming.
Typescript can help by implementing a refactor feature Generate Minimal Interface
. Typescript already has all the information needed in the function to know which properties are being used by this function, and what types the are. We just need to combine all that information into a new interface. Tedious for human beings, simple for a type system.
Now with the new feature, you can start with each function renderTable
calls, replace the User
type with a generated minimal interface. Then finally, in the renderTable function, create the TableRenderable
interface.
Instead of 10+ minutes in many of my real use cases, decoupling functions from the User interface could take a minute or 2.
💻 Use Cases
What do you want to use this for?
Decoupling interfaces from functions much more quickly.
What shortcomings exist with current approaches?
Decoupling in this manner is manual, as far as I am aware, which is ok, but it’s something that a computer could do just as easily.
Issue Analytics
- State:
- Created 2 years ago
- Reactions:3
- Comments:8 (3 by maintainers)
Top GitHub Comments
@ericwooley no, AMF is sort of the default state for suggestions that don’t need design work but don’t currently meet the bar for adding to the codebase
@RyanCavanaugh Is there anything I need to add to get past the “Awaiting More Feedback” label?