In a recent tech-sharing session with the colleagues I work with, I was shown a comparison of interface and type, and it led me to revisit an earlier decision to use interface for our project's object type definitions.
I don't remember exactly when that decision was made, but after discussing and reviewing it together, we decided to use interface. In the recent tech-sharing session, the takeaway was that the decision back then had been a wrong one, and that from the next project onward it would be better to use type.
Looking back on this, I want to briefly summarize why my view changed from the earlier decision.
The decision to use interface
As I recall, the discussion about interface versus type first began from an opinion that, since both forms were being used together for state managed as objects or for API data model type definitions within the project, interface and type were being mixed too much, so why not unify them into one.
The discussion of interface versus type has long been one of the hot topics, and I had actually summarized it on my blog before.
Based on what I summarized at the time, the TypeScript official documentation also recommended using interface for object type definitions whenever possible, and using type for cases like unions or tuples that cannot be defined with an interface.
- interfaces vs type aliases (there may be mistranslations on my part, so please check the older original text)
- interfaces vs type aliases (as of writing, the latest original text also guides toward using interface in a similar way)
Based on the above (having checked the original text), I explained it to my colleagues, and as above we decided to mainly use interface and to use type only for types that cannot be defined with interface.
As an actual use case, it was useful for defining Props of components that take an inheritance form.
Of course, the form below can also be defined and expressed with type.
tsx// Button.tsx
export interface ButtonProps {
text: string;
}
const Button = ({ text }: ButtonProps) => (
...
);
// LinkButton.tsx
import { ButtonProps } from './Button.tsx';
interface LinkButtonProps extends ButtonProps {
link: string;
}
const LinkButton = ({ text, link }: LinkButtonProps) => (
...
);
Let's use type
After hearing the tech-sharing that using type would be better, I looked into the issue again and easily found various opinions.
- Type vs Interface: Which Should You Use In 2023?
- Types vs. Interfaces in Typescript: Making the Right Choice
- etc…
interface supports inheritance via extends and definition as a class via implements.
tsxinterface Animal {
name: string;
}
interface Bear extends Animal {
honey: boolean;
}
const bear = getBear();
bear.name;
bear.honey;
However, simple inheritance like the above can be handled similarly using type's intersection to extend or combine types.
tsxtype Animal = {
name: string;
};
type Bear = Animal & {
honey: Boolean;
};
const bear = getBear();
bear.name;
bear.honey;
As above, unless you need deeper inheritance handling using interface rather than simple inheritance, using type is sufficient. Of course, with type you can also define every type through repeated intersection operations (&).
Next was the problem of interface merging. When an interface is defined with the same name within the same scope, it is processed as declaration merging. Since this merging happens without any problem during the TypeScript compilation process, it could potentially cause unexpected issues. In fact, declaring under the same name is itself the problem, but when using interface you cannot check this explicitly, whereas when using type it explicitly raises an error that you can catch.
There was also talk that in such cases in TypeScript, interface is cached by its declared name so that its compilation process performs better than a type declaration, but many articles say that performance can vary depending on the type, and that currently the performance difference is not significant.
Finally, with type you can define every type available in TypeScript, including tuples, unions, and definitions using computed property names as keys in objects like the one below.

Summary
Using interface
While organizing these points, I started again from the question: why should you not use interface? It's not that there's a premise that you must never use it, but I picked out the parts that seemed to be problems (?) with interface.
Declaration merging does not occur unless the declarations are within the same space (file, block), and type definitions are usually placed at the top of a file. If you happen to declare an interface with the same name, and its inner members are not optional or nullable, an error will occur at the compile stage in the objects using that interface, so I think you would be able to recognize it.
tsxinterface Error {
message: string;
code: number;
}
interface Error {
type: string;
}
// Error occurs
// Property 'type' is missing in type '{ message: string; code: number; }'
// but required in type 'Error'.
const e: Error = {
message: "Error!",
code: 400,
};
In the case of a React component, if an interface with the same name as the Props type exists, you can declare and use members from both interfaces in Props. But the type definition for a component's Props is usually located right above the component, so I wonder whether there would ever be an occasion where a Props interface with the same name is declared. In fact, I don't recall any duplicate interface declarations occurring during the project.
tsxinterface ButtonProps {
text: string;
type: string;
}
interface ButtonProps {
color: string;
}
// No additional error occurs even if color is not used inside the Button component
const Button = ({
text,
type
}: ButtonProps) => {
...
};
The last point is that if you want to unify all type declarations in the project into one, interface can only define object types, so it is effectively impossible; if you have that need, you must unify everything with type.
Using type
When specifying types using type, based on what I looked into, there were no major constraints at a basic level. This is because every form of type provided by TypeScript can be expressed with type.
If I had to pick one thing, it would be roughly that TypeScript recommends interface for object-shaped type definitions. And there are situations where using interface via implements is more appropriate for class definitions, but class-style definitions are hardly used in the current project.
In conclusion
Honestly, personally I do think that defining object types with interface as a notation for objects, and using extends for object inheritance and extension, is better in terms of code interpretation and readability. But there's no problem with using type either, and when I compared the two again above, I felt there were no major issues.
The reason I organized and reconsidered this again is that, while I'm currently leading the project, I don't think I gave enough thought to the impact each decision has on the colleagues I work with. Of course we review together and decide on the direction, but I felt I should make an effort to consider more detailed review and reasoning and to present a direction, so that a decision made at the time doesn't turn out to feel like it was the wrong one.