2

I'm fairly new to strongly typed languages, coming from the "jungle" world of JS and PHP. In my current Typescript project(React-Typescript), i'm struggling with a very fundamental question: Should every piece of code expose its own interface, even if that means having duplicate types across my application?

Simple example:

//API
interface TodoInterface {
 id: number,
 title: string,
 completed: boolean
}
function getTodos(): Array<TodoInterface> {
 return [{ id: 1, title: 'title', completed: true }, { id: 2, title: 'title', completed: true }]]
}
//Store
interface TodoStoreTodo {
 id: number,
 title: string,
 completed: boolean
}
class TodoStore {
 todos!: Array<TodoStoreTodo>
 async getTodos() {
 const todos = await getTodos()
 this.todos = todos;//Types are compatible, so this works.
 }
}
//main view
const todosStore = new TodoStore()
const Todos = () => {
 const { todos } = todosStore
 return (
 todos.map(todo => <Todo todo={todo} />)
 )
}
//Todo view
interface TodoProps {
 todo: {
 id: number
 title: string
 completed: boolean
 }
}
const Todo: React.FC<TodoProps> = (props) => {
 const { todo } = props;
 return (
 <div>
 <p>Id: {todo.id}</p>
 <p>Title: {todo.title}</p>
 <p>Completed: {todo.completed}</p>
 </div>
 )
}

Note that the same exact Todo "entity" is declared in 3 different places: The API function that fetches todos, the store that stores todos and the Todo component.

On one hand, this makes sure that each module/function/component is independent and "blind" to the outside world, but on the other hand this creates some repetition(not of logic of course, but still)

A radical alternative to this, would of course be having some global Todo interface, which will be "used" by all relevant parts.

It's like, there is some struggle between DRY and the will to avoid spaghetti code.

Any suggestions, explanations, or tips will be highly appreciated.

lennon310
3,2427 gold badges18 silver badges35 bronze badges
asked Oct 5, 2021 at 21:08

1 Answer 1

6

The same design concepts apply to types and runtime variables.

Depending on the relation and diversity of these WET types, base extendable and/or generic common types can be defined. It's not incorrect to define them for the whole application or its part, but as opposed to global types, common types are supposed to exported from a shared module.

E.g:

// base type to extend
export interface BaseTodo<T extends any> {
 id: number,
 title: string,
 completed: boolean,
 meta?: T,
}
// most commonly used type with no meta
export type SimpleTodo = BaseTodo<never>;

And imported where they are used:

import type { BaseTodo } from '.../common/types/todo.ts';
type PublishedTodo = BaseTodo<{ published: boolean }>;
// Since it's not practical to exclude id from common type,
// this can be done for a specific type
interface PublishedTodoDraft extends Omit<PublishedTodo, 'id'> {
 edited: Date
};
answered Oct 5, 2021 at 22:02
3
  • So you would place a generic Toto type, somewhere in the root of the application? But what happens if suddenly some "consumer" of this type needs to slightly change its structure? For example, imagine you have some property that references other types. In one place the reference is an array of ids(foreign keys), whereas in another place it's an array of the referenced objects(the entire referenced entity? Commented Oct 5, 2021 at 22:14
  • You can extend a base one with no changes if generic params cover this (the example with meta), or partially extend it they don't (e.g. use Omit util for incompatible keys), or create a new type if the difference with base one isn't worth extending. It's really the same as with runtime JS things like classes. Commented Oct 5, 2021 at 22:19
  • I added a clarification. I guess this can be translated easily to your case with references. Commented Oct 6, 2021 at 8:05

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.