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.