I'm just learning TS and pretty much most basic tutorials or articles I've found just say "types and interfaces do the same thing", so this video definitely caught me by surprise
I think its varied a lot over the development of Typescript what they can do, which probably adds to the confusion. Personally I think it was a mistake in the language design to have two things for this.
@@benjidaniel5595 Not only 1, but they also handle conflicts much sooner than what you would get with types. But as a general rule I use types most of the time, I like to think of types with unions and intersections much more than with interfaces.
Bytegrad does too sorta. Mostly he just discusses how to achieve the same results though. His take away is that it's best to be consistent so chose one vs the other. Gets interesting when you use shadcn though since all of its boilerplate is extended with interface
@@happyfase I think when you need to extend global types, such as the Window type. Or extending a library (or creating a library which should be possible to extend) I suppose.
@@happyfase use interface if you think the implementation type will have multiple version. Like general type animal thing. Like walk, fly and swim. Walk speed each animal can be differend. not all animal can fly. not all animal can swim too deep.
@@karmasakshi What he means is that type hints are not expanded when using interfaces. Although they are also not expanded when you intersect or union types together: interface SomeInterface { foo: number; bar?: string; baz: number | undefined; } type ExampleInterface: SomeInterface; // => SomeInterface (not expanded) type A = { a: number }; type B = { b: string }; type SomeIntersection = A & B; // => A & B (not expanded) To get around this you can convert interface into a type, as well as simplify the intersection types by looping over them to combine them: type Simplify = { [K in keyof T]: T[K] }; type SimplifiedInterface: Simplify = literal; // => { foo: number, bar?: string; baz: number | undefined; } const SimplfiedIntersection: Simplify { a: number, b: string };
When the video is the normal distribution meme with the newbie and the guru saying "use types by default" btw great content as always, love that you took on this journalistic effort, it's perfect to keep the pulse on TS world. Thank you!
2 года назад+43
I use interfaces only in a 'classic' OO sense: describe abstractions which multiple classes can inherit and provide the functionality described by the interface. These are usually 'classic' OO objects with private data, getters/setter and member functions ... no 'weird' JS stuff :). For all other purposes I find types to be superior (mainly due to discriminating unions). I also find describing a function via interface weird personally so I would always do it via type alias.
phase 3 is where it's at, imo you should default to types and use interfaces when you have a specific need for them. note also that a class can implement a `type`, and sometimes you want to do this - in my experience you usually want an interface when doing classical OOP (i.e. not implementing arbitrary types) or when augmenting a module to merge an interface declaration. it's also worth noting that all object types (whether declared via `type`, `interface`, or inline) are open to extension, meaning that they can always contain extra properties that are not declared on the type. this is a feature of the language and it's the reason why Object.hasOwnProperty doesn't return a type predicate and Object.keys doesn't have a narrower return type.
There is also a difference in error handling. If you have 2 interfaces with property with the same name but different type, you will get a compile error. If you do the same with types, you will get a never type.
Which you'll later spend 15 minutes on debugging/tracking down just to find out about this small little detail you've missed... Kind of reminds me of tracking down silly typos in plain JS... Reference errors all over the place due to fast typing, and always in the exact wrong moments.
Great explanation. Thank you, Matt. I've always been on the side of "use types for everything except if you want to use "class implements interface" feature. My reasoning was simply I don't have enough reasons to use interfaces for anything else.
Typescript docs recommend to use interfaces until you need types because interfaces have more readable errors. Would love a follow up with this considered
If anything the example given at the end makes me appreciate interfaces more, since to me it points out the flaw in the example. If I already know the data's structure well enough to be typing it (such as MyData in the example), then I'm more likely just going to modify the JSONValue type and the handle function to be generics that can take which exact set of keys I'll be expecting rather than just "[key: string]" to get better type-safety. If you only need the function to handle those 4 options then you don't even run into the problem. Personally and the main reason I 100% prefer interfaces is I think interfaces visually are much cleaner, and even eye-catching in comparison to the rest of the code. "type FooBar = {}" and "const fooBar = {}" are SOOOO similar, that it visually homogenises the code just that tiiiiny bit more than I'd like. Taking that one step further, extending interfaces is also more visually distinct and IMO less cluttery than "&" characters slotted between types. Even separating those concerns into their own files can sometimes create a different type of problem where if you're moving very quickly through your code (jumping through go-tos, cursor back-forths) that when everything has the same structure and (depending on your editor) the same colours you can't identify certain things as quickly. In the end mainly due to the very particular and petty reasons above I'm in the "use whatever!" camp. Unless you have some very specific use-cases, then using whatever is perfectly fine!
This was my approach for quite some time, the risk of working with types is when you extend an object. with type you can override any property of the extended object, while interface will not let you do this, so I'm using interface unless I know I want to override one of the extended object properties (or do something else that only type can do)
I'm a noob so of course at first (last week), I was using interfaces for anything and everything more than a one line type definition. I've been coming around to your way of thinking more over the past few days and I feel better after hearing what you have to say about it. Thanks
I also went back and forth between both for a while, but ultimately settled for something like you did. Use types when I need their features, use interfaces when I need their features. And, if I absolutely don’t need any of their features, that actually raise the question why and we try to figure out if we truly need that new type/interface in the way we see it. Oftentimes we realise that processing an existing type trough a generic or a mapped type.
For me is weird using types for everything because I always though interfaces are the entrypoints for connecting things, the shape of the IO of a thing, and types were entities. But I always ended up using types to avoid the augmentation, because that is tricky to visualize and handle sometimes (at least for me).
Sounds like interface is better for long term project as it is scalable. For my personal project I would use type but a project other people actually use I would use interface because I’ll probably need to make changes in the future
types work better with Visual Studio intellisense so when you hover over something that has a type it shows you the type definition, whereas with an interface you have to navigate to the interface to see it’s definition
I just found out that if we hover over the type itself, yes we get the whole type definition, but if we hover over the variable defined with the type, it just shows the name of the type, unless we press Ctrl before hovering. I learned this shortcut from @Ben Haynes in this comment! Thanks!
Loved hearing your journey with this decision! It was very relatable, because I feel like I flip flop like this with everything in my life, not just technology...😅
Matt, what are your thoughts on organizing types and interfaces in a project and naming? Separate /types and /interfaces folders? What about file naming? use name.ts or name.interface.ts / name.type.ts?
Jon, please don't put all your types in a single file (unless they can all fit on the screen without scrolling while in an editor). that's a bad practice and terrible to see in enterprise code regarding folders and naming, it really depends on the project, how many files there are, and the team - if they're models, just put them in a /models folder like `/models/name.ts` - if the models folder grows to like 10+ files, then consider reorganizing the files
One thing that interface is better than type (arguably) is when you want to hide the complexity of the types in IDE. type is type alias and they will expand by default when you hover over the variable (in normal cases, there are exceptions). for interface, it will show the name of the interface only. It's a "it depends" case really, because in function arguments, types is typically better because you can see what you need inside the variable. On the other hand, when reading a variable interface can be cleaner. However, I still stick with what I recommend. Use type over interface.
One could argue that it's desired to simplify the type because it improves type hints, which otherwise would require you to poke into the type before you know its shape. One problem you can end up in when using interfaces, though, is when you try to pass a value that is an interface type into an wider type, i.e. a Record in a function argument, even though they may have identical shapes: interface SomeInterface { foo: number; bar?: string; baz: number | undefined; } const literal = {foo: 123, bar: 'hello', baz: 456}; const someInterface: SomeInterface = literal; function fn(object: Record): void {} fn(someInterface); // Error: Index signature for type 'string' is missing in type 'someInterface'. Because `interface` can be re-opened To get around this you can simplify the type by converting the interface to a type: type Simplify = { [K in keyof T]: T[K] }; fn(someInterface as Simplify); // Transform an `interface` into a `type`, resulting in hapiness On top of this is can be used to simplify the types for intersection and union types, which would otherwise also hide the shape: type A = { a: number }; type B = { b: string }; type AB = A & B; // => A & B type ABSimplified = Simplify // => { a: number, b: string }
Module augmentation can be used or abused. Good example is MUI Material library, where you can extend a component's prop interface without the need of reexporting the component or the interface, so you can use those props in styling
I use types for most things because they are strict, which tends to keep objects cleaner and enables many useful utility functions that rely on knowing exactly which properties exist and don't exist on an object. Interfaces are best used when you explicitly don't want control over the properties of an object. If it doesn't matter I'll default to types.
this video is very instructive for someone that doesn't went deep into types but still appreciate how is the common sense related to this way of typing things
Same here. While I do think it's very important to be consistent in your codebase, I will not prefer types over interfaces simply because they're more powerful as I see them convey 2 different ideas. Types are just shorthands for whatever you define them to be and interfaces define a forced / non-dynamic structure for an object. Therefore I use them as such.
Does this discussion with the Typescript team happened to be somewhere on the GitHub or it was private? Asking because it would be interesting to read the whole thread
I've had some niche little quirks where using an object intersection would break a type guard function, but rewriting those same types as interfaces with inheritance worked just fine. I have no idea why it behaved that way, but interfaces fixed it so those will forever be interfaces.
"If you want something predictable that's never going to act strange or do wierd things in certain situation then use types" I'm going to counter that by saying that if you don't want wierd and strange behaviour then avoid Javascript entirely lol.
This has been my approach for the last 2 years, only use Interfaces when you need them specifically for one of their unique features. Beforehand I was captain interface for everything! 😂
I put together some bullet notes on this video: docs.google.com/document/d/e/2PACX-1vQ6KyeWu-2MiHPe_9187HsXP7xdLyD3mRPjaWG70kJSXBHXKAXfWnnUCZnOHwLQSdePjGTlnGHwP9xH/pub Should you use Types or Interfaces? =============================================== By: Matt Pocock Notes recorded on: 2023-05-05 * Matt has changed his mind on the Types or Interfaces discussion. * He’s been through three phases. * Phase 1: Interfaces are the bomb. * Phase 2: It doesn’t matter which one you use as long as you’re consistent. * Phase 3: Use types unless you need a specific feature of interfaces. Phase 1: Interfaces are the bomb. --------------------------------- * Phase 1 was endorsed because they optimized performance of the typechecker. At some point in TypeScript, if you had a ton of types, your typechecker could run very slowly. * Matt ran a benchmark, which he admits was problematic, but he discovers that the conjecture of phase 1 was no longer true. * Matt says the TypeScript team said that this advice is no longer relevant. Phase 2: It doesn’t matter which one you use as long as you’re consistent. -------------------------------------------------------------------------- * Every object should be typed as an interface. * Everything else should be typed as a type. * Or you can use types for everything. * Matt mentions that interfaces have more features than types. * One interface can easily inherit from another interface. * Types can do this, but require the overhead of intersections. * Matt frames the feature set of interfaces by saying that “interfaces come bundled with a bunch of features that you probably don’t care about.” * He cites declaration merging as an example. * This feature allows TypeScript to support the use case of appending properties to an existing object, such as the browser’s window object. * The inventor of Angular asked Matt a TypeScript puzzle that arose because interfaces can be appended to. * “You don’t actually know what’s going to be in an interface when you use it.” * This problem introduces phase 3. Phase 3: Use types unless you need a specific feature of interfaces. -------------------------------------------------------------------- * If you need a type that extends another type, use an interface. * If you need a class that extends an interface, use an interface. * If you want something stabler, then use types. * Don’t worry about the performance difference. * Matt will be using types to declare most things.
Thanks for the video! I’ve been searching about types x interfaces for a quite but you were the first who actually pointed a real use case of both. That being said, is there a “semantic” (in terms of good practices or convention)use case for types/interfaces apart from its inner functionalities?
In objecty-oriented languages interfaces are meant to be used to describe what a certain thing (class) can do. It doesn't describe structure but behavior. Thus, using it to describe fields in the object is not a good practice - types suit it better.
My thought is that types should be preferred unless you need to do abstraction/contract where you could have potentially multiple implementations of the same contract. Caring about performance is bad for code readability here. Even if it does differ. We trade readability for runtime performance even... type is just keyword for any TYPE. if we had normal complex runtime types in js, there would be no such thing as declaring specific object shape type and using it as contract for something. That would be just abstract class type of things (from oop world). Interfaces is more semantic thing than technical. It means abstract stuff out. In c++ they even do keyword redifinition, so class and interface is completely same thing. Differs only on semantic level, on technical side it's just pure abstract class. From this perspective i prefer types for declaring props in react, yes it's objects all the time, but they are not abstractions - they are concrete specific types (it's more like DTO objects for passing arguments)
I've almost exclusively used Types for a looong time. Yeah I've used interfaces indirectly and also in the VERY seldom situation where I needed to eg extend a global interface, but otherwise it's all been types. And for my part a type union is what interface extension should always have been, and with its connected operations is just better. The ONE thing where I sometimes miss interfaces is that they can be self-referential, and most of the times that probably should be avoided anyways. But if you've got an interface that has a few fields that use similar types or something like that, you can use this.field to refer to the type of 'field'. That probably is possible due to the same mechanic as interface extension now that I think about it. Anyways, that has been somewhat fascinating for me. But it's not neccesarily all that predictable and in many instances you'll find other ways to express that stuff that are just a bit more usual and predictable.
Hm, never thought so much about it, but I never considered one to be better or worse in general. Just like in your conclusion, I use what's best suited for the purpose. Besides that, the very frequent zooming makes me a bit seasick, but I am also old ;-)
I promote using types instead of interfaces to describe Props in React just because of merging, Props types are finite, like saled classes in other languages Performance suffers only if they are complex, interfaces are cached, types are not
I assume interfaces getting merged can be useful in places where npm packages can extend functionality for some lib without re publishing the original lib, as some sort of plugin system that can be made a bit more broadly version compatible.
Another reason why I’m starting to use types by default is: interface SomeObject { someProperty: string } is not assignable to Record But the type version is: type SomeObject = { someProperty: string }
Indeed. It's very annoying. But if you for some reason have to consume an interface, you can convert it into a type by simplifying it: type Simplify = { [K in keyof T]: T[K] }; function fn(object: Record): void {} fn(someInterface as Simplified); // Now this works!
1:58 what do you mean by "mess about with intersections"? Intersection is a standard TS feature, isn't it? I'm not sure why you presented this solution as kind of a hack. Thanks for the video. All the best
I use 'mess about' in the British, playful sense - not trying to infer there's anything wrong with intersections. They're just as powerful as 'extends'.
@@mattpocockuk I see, as you can see by my name English is not my first language :) From your phrasing, I inferred that you're saying that intersections are a somewhat lesser, more tricky, feature. Thanks for the answer! :)
This is why I use types type x = { firstName: string} And if I change my mind to do type x = string Only types can do that Plus the type utilities that I use a lot
I prefer Interfaces simply because I don't have to write '=' when declaring one. As a developer, shaving 0.2 seconds is something I cannot take for granted.
there are definitely differences between types and interfaces, interface must have statically known properties while types don't, so you can have type A = { [k in K]: O[K] } you can't really define this type using interfaces, right? or am I missing something?
I think that's fine and it aligns with his advice. I think this video was for those ambiguous or subjective circumstances. In your case, it's absolutely clear you'd need an interface.
As someone who prefers Interfaces, I wish there is a way to either A) Mark a setting that interfaces are not mutable and disallow the default merging feature unless flagged inline, or B) Add a keyword inline to specifically disallow that behavior on that interface and in extension create naming collisions. Examples: ------------------------------------------- Option A) // tsconfig.json { ... "disallowInterfaceConsolidation": true ... } /* * Because both "Interfaces" are prefaced by a "mut" keyword, they are mutable, unlike any other Interfaces * that would otherwise throw an error on compliation saying they cannot be merged. */ mut Interface Animal { sound: () => void; } mut Interface Animal { furType: "feather" | "fur" | "bare" } ------------------------------------------- Option B) // * Normal, current behavior /* * The code below would cause a TypeScript Error because Animal was set with an "immutable" keyword, * but was declared twice at the same scope/level. * * Could also use "unique" as the keyword, maybe. Not sure what they keyword should be, * just something to say it can't have a sibling level duplicate. */ immutable Interface Animal { sound: () => void; } Interface Animal { furType: "feather" | "fur" | "bare" }
I tend to rarely use interfaces even though the extension syntax for interfaces is a bit cleaner, I usually just use the intersection operator. I've resolved to only use interfaces for implementing classes which I rarely do because I don't write a lot of classes (oop is the devil (class is still a good tool, just don't orient your entire program with them)).
I had switched to types but then switched back to interfaces since I had a Type named User and a React Component also named User. Now I have an interface named IUser and a React component named User.
I'm so glad you made this I ran into this last year and kinda used both in the same project I couldn't figure out what then difference was But... Everywhere you look says use an interface Now I get it use Types in JavaScript / react the majority of times for safety reasons Then use interfaces pretty much in every other language 😮😊
For anyone trying to dive deep into why a Type works but an interface won't, I quote one of the members of the TS teams from an issue of 2017 in this regard "this behavior is currently by design. Because interfaces can be augmented by additional declarations but type aliases can't, it's "safer" (heavy quotes on that one) to infer an implicit index signature for type aliases than for interfaces. But we'll consider doing it for interfaces as well if that seems to make sense"
Coming from an OOP like C# and Java, an interface should be used when you want to define methods with no implementation and let the class inherit the interface and implement the methods, use type as the data object. That is how I see it.
Yes, haha I love the word interface, and that's why I use interfaces over types. Thanks for your video though, I wasn't even aware of these differences between them
I just found this amazing blog, so happy with its valuable content. I was wondering would u mind to prepare one about Hexagonal(Dependency Inversion) in TS? I come from OOP and I find TS really powerful but at times I miss the stability that brings Hexagonal and Service contracts in the form of Interfaces
Type has four character, Interfaces has 10 character. what a obvoius choice! Type is always my first choice. I use interface when you cant use type. In my expirence in typescript I havnt encounter that situation.
I care about program speed, not speed of type checker. The type checker should be optimized as time goes by, but why should I care about the type checker when I already have a lot to care about by program?
I've had a conversation with GPT3 about this, and we came to the conclusion interfaces are better suited for defining contracts for classes, where types are better suited for defining contracts(shapes) for values; this would make interfaces a higher abstraction level than types. This completely ignores the quirks of interface merging and such, but what do you think of this?
that's funny. I just asked ChatGPT and it suggested using interfaces over types for both: > "In summary, both interfaces and types can be used to define models in TypeScript, but interfaces are generally preferred for their simplicity, readability, and wider community adoption. Types are more flexible and can be used to define more complex type structures, but are typically used for more advanced type operations."
@@RoyRope Here are my two questions before I got that response: - In TypeScript, what is the best method of defining models or contracts? - but what about Types?
@@lamspam ah oke interesting, sadly I asked GPT before the chat history existed. But I remember having a conversation asking about the pros en cons of types and interfaces and the different levels of abstaction inside a tyepscript program.
Seems we both came to the same conclusion. I used to use interfaces but I ran into werid instances where types just worked better and now I use types for everything unless I need a specific feature of interfaces.
So, I agree, "and" I would likely go a bit farther and say it would be best if you can simply use "types" all the time. Why? Well, basically because those things you mentioned Interfaces have that types don't are things that, IMHO, are things that should be avoided in JS as much as possible (for those who are about to argue that this is TS, not JS, TS gets transpiled to JS, and that's important to realize). I feel like just about everything that has gone wrong with JS started the very minute they tried to make it look more like JAVA (and that goes all the way back to the constructor function that was added to original JS). In my JS experience (which is over 25 years), every time I have tried to implement common OOP techniques like polymorphism and inheritance, very, very bad things eventually happened. The keyword there is eventually. The problems don't manifest right away. The main things that manifest early is slower code, and I could almost always point out a JAVA developer who was hating on JS because of its "slowness" because I knew they were doing it wrong. When ECMA capitulated and added the "class" keyword I thought, "here we go". That practically gave a license to use JS wrong. In short, composition and factory functions continue to be easier to maintain in the long run than inheritance (in JS/TS) and that's basically because JS is not object oriented. it's prototypal and the difference doesn't seem big until it does, but by then, there's not much you can do about it without lots of expense. It's a rarity when entire teams are so disciplined that their OOP code of JS doesn't get out of control (same with non-oop, actually, but many oop folks seem to think that they are magically protected from being undisciplined). Also, there is one other benefit I've found to types that interfaces rarely have, and it has to do with the IDEs themselves. In my experience, many IDEs tend to give you more information about types than they do about interfaces when you hover over parameters, properties and variables that were declared with a type, vs an interface. Often, when it's an interface, all I will see is an interface name, but when it's a type, I'll often see the properties, restrictions, etc from that type. It's a subtle distinction and may depend on the IDE you use, but it makes a difference to me.
So I got asked on one of interviews - when should I use types over interfaces and is the difference? I responded with almost same points but interviewer was not satisfied and responded that I can achieve exactly same things with types and intersections. In this video I was hoping for clarification on this one but seems like interviewer was just bullying me. Still don't get what he wanted from me.
It's not even neccessary for declaring that a class implements a type or that an interface extends from a type that the type being extended from is an interface. As long as the result is a plain object type, it can be extended from.
Would it be helpful(and solve the problem mentioned in the video) if we had some keyword to disallow interface extension? Something like "final" in Java? (Not meaning that you can't extend from the interface), but that you can't amend properties to existing interface.
It's pretty tough, because ALL projects have some kind of declaration merging. It's present in lib.dom.d.ts, and libraries which depend on it. It's a critical part of library code. It's also pretty hard to lint for outside of TS, too. So it's a tough problem to try and restrict it. I'd argue that a type _is_ a 'final' interface.
yeah, thinking about it there is probably a very minimal amount of places where this would be really helpful, and for all others just using Type would kind of do it.
Why would you want this though? Declaration merging is a feature of the language - you can do it with interfaces, functions, and probably other things as well. If you want to disallow declaration merging for a type then you can declare it as a `type`. If you're writing a library, please don't declare everything as `type` just to prevent declaration merging unless you're sure it's the right thing to do. I've used too many libraries that expose an `any` where they shouldn't, and there's little I can do about it downstream because they've opted for `type` everywhere.
We actually used to use abstract classes instead of interfaces in Typescript as they can also just be implemented and don't have this. Well we also didn't use much experience with Typescript overall that point and today I would scrap that idea.
I don't mean to be that guy but it would be nice at 1:21 if you just showed the actual correspondence between the typescript team and you instead of giving a "x is a kind of y" statement
Finally someone actually highlighted that Interfaces have different features from types. Good bless this man.
I'm just learning TS and pretty much most basic tutorials or articles I've found just say "types and interfaces do the same thing", so this video definitely caught me by surprise
Interfaces really only have 1 feature, interface merging. Types can do everything else and much more
I think its varied a lot over the development of Typescript what they can do, which probably adds to the confusion. Personally I think it was a mistake in the language design to have two things for this.
@@benjidaniel5595 Not only 1, but they also handle conflicts much sooner than what you would get with types. But as a general rule I use types most of the time, I like to think of types with unions and intersections much more than with interfaces.
Bytegrad does too sorta. Mostly he just discusses how to achieve the same results though. His take away is that it's best to be consistent so chose one vs the other. Gets interesting when you use shadcn though since all of its boilerplate is extended with interface
This is my preferred approach. Use types by default, and interfaces only when necessary. I find type intersections and unions to be very useful
But when is it necessary?
@@happyfase I think when you need to extend global types, such as the Window type. Or extending a library (or creating a library which should be possible to extend) I suppose.
@@happyfase Tell me you haven't watched the video without telling me that you haven't watched the video.
I find interfaces to be much cleaner when reading errors. Especially when there is a lot of properties.
@@happyfase use interface if you think the implementation type will have multiple version. Like general type animal thing. Like walk, fly and swim. Walk speed each animal can be differend. not all animal can fly. not all animal can swim too deep.
Small note: `types` unwraps to the underlying structures, `interfaces` preserve the "box" name throughout
I’d like to understand your comment better. Can you give an example please?
@@karmasakshi What he means is that type hints are not expanded when using interfaces. Although they are also not expanded when you intersect or union types together:
interface SomeInterface {
foo: number;
bar?: string;
baz: number | undefined;
}
type ExampleInterface: SomeInterface; // => SomeInterface (not expanded)
type A = { a: number };
type B = { b: string };
type SomeIntersection = A & B; // => A & B (not expanded)
To get around this you can convert interface into a type, as well as simplify the intersection types by looping over them to combine them:
type Simplify = { [K in keyof T]: T[K] };
type SimplifiedInterface: Simplify = literal; // => { foo: number, bar?: string; baz: number | undefined; }
const SimplfiedIntersection: Simplify { a: number, b: string };
When the video is the normal distribution meme with the newbie and the guru saying "use types by default"
btw great content as always, love that you took on this journalistic effort, it's perfect to keep the pulse on TS world. Thank you!
I use interfaces only in a 'classic' OO sense: describe abstractions which multiple classes can inherit and provide the functionality described by the interface. These are usually 'classic' OO objects with private data, getters/setter and member functions ... no 'weird' JS stuff :).
For all other purposes I find types to be superior (mainly due to discriminating unions). I also find describing a function via interface weird personally so I would always do it via type alias.
Same here!
phase 3 is where it's at, imo you should default to types and use interfaces when you have a specific need for them. note also that a class can implement a `type`, and sometimes you want to do this - in my experience you usually want an interface when doing classical OOP (i.e. not implementing arbitrary types) or when augmenting a module to merge an interface declaration. it's also worth noting that all object types (whether declared via `type`, `interface`, or inline) are open to extension, meaning that they can always contain extra properties that are not declared on the type. this is a feature of the language and it's the reason why Object.hasOwnProperty doesn't return a type predicate and Object.keys doesn't have a narrower return type.
Man, this video just put an end to all the madness I've been through. Thank you very very much.
There is also a difference in error handling. If you have 2 interfaces with property with the same name but different type, you will get a compile error. If you do the same with types, you will get a never type.
Which you'll later spend 15 minutes on debugging/tracking down just to find out about this small little detail you've missed...
Kind of reminds me of tracking down silly typos in plain JS... Reference errors all over the place due to fast typing, and always in the exact wrong moments.
Great explanation. Thank you, Matt. I've always been on the side of "use types for everything except if you want to use "class implements interface" feature. My reasoning was simply I don't have enough reasons to use interfaces for anything else.
KISS FTW.
This is 100% my philosophy for interacting with types and interfaces. Awesome Video!
Typescript docs recommend to use interfaces until you need types because interfaces have more readable errors. Would love a follow up with this considered
Or, they could at least implement syntax highlighting and properly formatted error messages
Those issues are stale for over a year now lol
If anything the example given at the end makes me appreciate interfaces more, since to me it points out the flaw in the example. If I already know the data's structure well enough to be typing it (such as MyData in the example), then I'm more likely just going to modify the JSONValue type and the handle function to be generics that can take which exact set of keys I'll be expecting rather than just "[key: string]" to get better type-safety.
If you only need the function to handle those 4 options then you don't even run into the problem.
Personally and the main reason I 100% prefer interfaces is I think interfaces visually are much cleaner, and even eye-catching in comparison to the rest of the code. "type FooBar = {}" and "const fooBar = {}" are SOOOO similar, that it visually homogenises the code just that tiiiiny bit more than I'd like. Taking that one step further, extending interfaces is also more visually distinct and IMO less cluttery than "&" characters slotted between types.
Even separating those concerns into their own files can sometimes create a different type of problem where if you're moving very quickly through your code (jumping through go-tos, cursor back-forths) that when everything has the same structure and (depending on your editor) the same colours you can't identify certain things as quickly.
In the end mainly due to the very particular and petty reasons above I'm in the "use whatever!" camp. Unless you have some very specific use-cases, then using whatever is perfectly fine!
This was my approach for quite some time, the risk of working with types is when you extend an object. with type you can override any property of the extended object, while interface will not let you do this, so I'm using interface unless I know I want to override one of the extended object properties (or do something else that only type can do)
I'm a noob so of course at first (last week), I was using interfaces for anything and everything more than a one line type definition. I've been coming around to your way of thinking more over the past few days and I feel better after hearing what you have to say about it. Thanks
I also went back and forth between both for a while, but ultimately settled for something like you did. Use types when I need their features, use interfaces when I need their features. And, if I absolutely don’t need any of their features, that actually raise the question why and we try to figure out if we truly need that new type/interface in the way we see it. Oftentimes we realise that processing an existing type trough a generic or a mapped type.
I use interfaces for classes and types for everything else... so yeah, I never use interfaces 😉
Well that's just lovely
I was like this too, hating on the classes. But they are actually powerful if you are willing to learn how to use them properly
What are classes?
I use interfaces for typing React component props, since I can extend the base HTML element props
@@producdevity☠️
I always did this without even thinking about it, it's awesome to know the Typescript guru also do the same.
For me is weird using types for everything because I always though interfaces are the entrypoints for connecting things, the shape of the IO of a thing, and types were entities. But I always ended up using types to avoid the augmentation, because that is tricky to visualize and handle sometimes (at least for me).
Amazing deep dive into something that one would never even thing of existing in the first place! Bravo, Matt 🍻
Sounds like interface is better for long term project as it is scalable.
For my personal project I would use type but a project other people actually use I would use interface because I’ll probably need to make changes in the future
types work better with Visual Studio intellisense so when you hover over something that has a type it shows you the type definition, whereas with an interface you have to navigate to the interface to see it’s definition
Because typs is just an alias and interface is ….. whatever i dont even onow
This. I hate to only see the name when I'm hover a type name and it just gives me. I'm mean, thanks captain 🙄
if you hold `command` (mac) or `ctrl` (windows) before you hover it will show you the interface definition
I just found out that if we hover over the type itself, yes we get the whole type definition, but if we hover over the variable defined with the type, it just shows the name of the type, unless we press Ctrl before hovering.
I learned this shortcut from @Ben Haynes in this comment! Thanks!
I'm also using the 3rd option and it's probably best practice how using types in typescript but for typing API json objects i'm also using interfaces.
Loved hearing your journey with this decision! It was very relatable, because I feel like I flip flop like this with everything in my life, not just technology...😅
Love it. Been using exclusively types for awhile mostly because of the consistency. Had no idea that interfaces even had these issues.
Matt, what are your thoughts on organizing types and interfaces in a project and naming? Separate /types and /interfaces folders? What about file naming? use name.ts or name.interface.ts / name.type.ts?
types.ts
Jon, please don't put all your types in a single file (unless they can all fit on the screen without scrolling while in an editor). that's a bad practice and terrible to see in enterprise code
regarding folders and naming, it really depends on the project, how many files there are, and the team
- if they're models, just put them in a /models folder like `/models/name.ts`
- if the models folder grows to like 10+ files, then consider reorganizing the files
One thing that interface is better than type (arguably) is when you want to hide the complexity of the types in IDE.
type is type alias and they will expand by default when you hover over the variable (in normal cases, there are exceptions).
for interface, it will show the name of the interface only.
It's a "it depends" case really, because in function arguments, types is typically better because you can see what you need inside the variable.
On the other hand, when reading a variable interface can be cleaner.
However, I still stick with what I recommend. Use type over interface.
One could argue that it's desired to simplify the type because it improves type hints, which otherwise would require you to poke into the type before you know its shape. One problem you can end up in when using interfaces, though, is when you try to pass a value that is an interface type into an wider type, i.e. a Record in a function argument, even though they may have identical shapes:
interface SomeInterface {
foo: number;
bar?: string;
baz: number | undefined;
}
const literal = {foo: 123, bar: 'hello', baz: 456};
const someInterface: SomeInterface = literal;
function fn(object: Record): void {}
fn(someInterface); // Error: Index signature for type 'string' is missing in type 'someInterface'. Because `interface` can be re-opened
To get around this you can simplify the type by converting the interface to a type:
type Simplify = { [K in keyof T]: T[K] };
fn(someInterface as Simplify); // Transform an `interface` into a `type`, resulting in hapiness
On top of this is can be used to simplify the types for intersection and union types, which would otherwise also hide the shape:
type A = { a: number };
type B = { b: string };
type AB = A & B; // => A & B
type ABSimplified = Simplify // => { a: number, b: string }
This is exactly why we use types over interfaces
Module augmentation can be used or abused. Good example is MUI Material library, where you can extend a component's prop interface without the need of reexporting the component or the interface, so you can use those props in styling
I use types for most things because they are strict, which tends to keep objects cleaner and enables many useful utility functions that rely on knowing exactly which properties exist and don't exist on an object.
Interfaces are best used when you explicitly don't want control over the properties of an object. If it doesn't matter I'll default to types.
first time viewer. good content and also pleasant to watch. Thank you
this video is very instructive for someone that doesn't went deep into types but still appreciate how is the common sense related to this way of typing things
I personally use interfaces for everything unless I need types (Intersections, Unions, Type aliases, inferences, e.t.c)
Same here.
Same here. While I do think it's very important to be consistent in your codebase, I will not prefer types over interfaces simply because they're more powerful as I see them convey 2 different ideas. Types are just shorthands for whatever you define them to be and interfaces define a forced / non-dynamic structure for an object. Therefore I use them as such.
Does this discussion with the Typescript team happened to be somewhere on the GitHub or it was private? Asking because it would be interesting to read the whole thread
2:34 Is ^? only a visual mark or it has some documentation meaning? I just can not find and I am super curious about it.
There's a canonical blog post on this 5 years ago. Good to see that advice hasn't changed
I use Type only. I used to use interfaces because I came from c#. But types as you said are predictable and just work nicely.
I've had some niche little quirks where using an object intersection would break a type guard function, but rewriting those same types as interfaces with inheritance worked just fine.
I have no idea why it behaved that way, but interfaces fixed it so those will forever be interfaces.
Could you please share the details of the lamp in the background?
"If you want something predictable that's never going to act strange or do wierd things in certain situation then use types"
I'm going to counter that by saying that if you don't want wierd and strange behaviour then avoid Javascript entirely lol.
Appreciate the candor, Matt!
I was wondering this 2 days ago and my conclusion was "why do we even need interfaces" but then you reminded me classes extend them 😄
I uses interface by default and types if I need a certain feature. Interfaces has a show reference feature which is useful to me
This has been my approach for the last 2 years, only use Interfaces when you need them specifically for one of their unique features. Beforehand I was captain interface for everything! 😂
I like that you said "If you like the word interface, use interface". More people should acknowledge your preference when giving advice.
I put together some bullet notes on this video: docs.google.com/document/d/e/2PACX-1vQ6KyeWu-2MiHPe_9187HsXP7xdLyD3mRPjaWG70kJSXBHXKAXfWnnUCZnOHwLQSdePjGTlnGHwP9xH/pub
Should you use Types or Interfaces?
===============================================
By: Matt Pocock
Notes recorded on: 2023-05-05
* Matt has changed his mind on the Types or Interfaces discussion.
* He’s been through three phases.
* Phase 1: Interfaces are the bomb.
* Phase 2: It doesn’t matter which one you use as long as you’re consistent.
* Phase 3: Use types unless you need a specific feature of interfaces.
Phase 1: Interfaces are the bomb.
---------------------------------
* Phase 1 was endorsed because they optimized performance of the typechecker. At some point in TypeScript, if you had a ton of types, your typechecker could run very slowly.
* Matt ran a benchmark, which he admits was problematic, but he discovers that the conjecture of phase 1 was no longer true.
* Matt says the TypeScript team said that this advice is no longer relevant.
Phase 2: It doesn’t matter which one you use as long as you’re consistent.
--------------------------------------------------------------------------
* Every object should be typed as an interface.
* Everything else should be typed as a type.
* Or you can use types for everything.
* Matt mentions that interfaces have more features than types.
* One interface can easily inherit from another interface.
* Types can do this, but require the overhead of intersections.
* Matt frames the feature set of interfaces by saying that “interfaces come bundled with a bunch of features that you probably don’t care about.”
* He cites declaration merging as an example.
* This feature allows TypeScript to support the use case of appending properties to an existing object, such as the browser’s window object.
* The inventor of Angular asked Matt a TypeScript puzzle that arose because interfaces can be appended to.
* “You don’t actually know what’s going to be in an interface when you use it.”
* This problem introduces phase 3.
Phase 3: Use types unless you need a specific feature of interfaces.
--------------------------------------------------------------------
* If you need a type that extends another type, use an interface.
* If you need a class that extends an interface, use an interface.
* If you want something stabler, then use types.
* Don’t worry about the performance difference.
* Matt will be using types to declare most things.
Thanks for the video! I’ve been searching about types x interfaces for a quite but you were the first who actually pointed a real use case of both. That being said, is there a “semantic” (in terms of good practices or convention)use case for types/interfaces apart from its inner functionalities?
In objecty-oriented languages interfaces are meant to be used to describe what a certain thing (class) can do. It doesn't describe structure but behavior. Thus, using it to describe fields in the object is not a good practice - types suit it better.
Great typescript video!
I was having an anxiety attack waiting for the final answer and just praying that it was *types.*
Phew, I can breathe easier now.
My thought is that types should be preferred unless you need to do abstraction/contract where you could have potentially multiple implementations of the same contract. Caring about performance is bad for code readability here. Even if it does differ. We trade readability for runtime performance even... type is just keyword for any TYPE. if we had normal complex runtime types in js, there would be no such thing as declaring specific object shape type and using it as contract for something. That would be just abstract class type of things (from oop world). Interfaces is more semantic thing than technical. It means abstract stuff out. In c++ they even do keyword redifinition, so class and interface is completely same thing. Differs only on semantic level, on technical side it's just pure abstract class.
From this perspective i prefer types for declaring props in react, yes it's objects all the time, but they are not abstractions - they are concrete specific types (it's more like DTO objects for passing arguments)
I've almost exclusively used Types for a looong time.
Yeah I've used interfaces indirectly and also in the VERY seldom situation where I needed to eg extend a global interface, but otherwise it's all been types.
And for my part a type union is what interface extension should always have been, and with its connected operations is just better.
The ONE thing where I sometimes miss interfaces is that they can be self-referential, and most of the times that probably should be avoided anyways.
But if you've got an interface that has a few fields that use similar types or something like that, you can use this.field to refer to the type of 'field'. That probably is possible due to the same mechanic as interface extension now that I think about it.
Anyways, that has been somewhat fascinating for me.
But it's not neccesarily all that predictable and in many instances you'll find other ways to express that stuff that are just a bit more usual and predictable.
BTW you can extend a type in a class definition
I prefer to choose interface for anything that can be thought as abstact API to an object and types for anything else.
Hm, never thought so much about it, but I never considered one to be better or worse in general. Just like in your conclusion, I use what's best suited for the purpose.
Besides that, the very frequent zooming makes me a bit seasick, but I am also old ;-)
So far I've only once had to use an interface... Types, and especially unions, are my favorite thing about TS.
you can do something like this though:
type Case = Base & {
title: string;
synopsis: string;
very good clarification thank you
I'm new to TS. Went with `type` as a default because it looks closer to vanilla JS than interfaces.
I started to use types more when I found out that the tooltips on vs code for types are much better.
Haha you just convinced me
I promote using types instead of interfaces to describe Props in React just because of merging, Props types are finite, like saled classes in other languages
Performance suffers only if they are complex, interfaces are cached, types are not
I assume interfaces getting merged can be useful in places where npm packages can extend functionality for some lib without re publishing the original lib, as some sort of plugin system that can be made a bit more broadly version compatible.
Another reason why I’m starting to use types by default is:
interface SomeObject {
someProperty: string
}
is not assignable to Record
But the type version is:
type SomeObject = {
someProperty: string
}
Indeed. It's very annoying. But if you for some reason have to consume an interface, you can convert it into a type by simplifying it:
type Simplify = { [K in keyof T]: T[K] };
function fn(object: Record): void {}
fn(someInterface as Simplified); // Now this works!
This is just what I was looking for! Thank you
When your course will be available?
1:58 what do you mean by "mess about with intersections"? Intersection is a standard TS feature, isn't it? I'm not sure why you presented this solution as kind of a hack.
Thanks for the video. All the best
I use 'mess about' in the British, playful sense - not trying to infer there's anything wrong with intersections. They're just as powerful as 'extends'.
@@mattpocockuk I see, as you can see by my name English is not my first language :) From your phrasing, I inferred that you're saying that intersections are a somewhat lesser, more tricky, feature. Thanks for the answer! :)
Nailed it.
TypeScript hero ❤️ .Thankyou so much legend
This is why I use types
type x = { firstName: string}
And if I change my mind to do
type x = string
Only types can do that
Plus the type utilities that I use a lot
you can also do it with interfaces, more verbose though
interface x extends InstanceType {};
@@iamprovidence-xj5wf yeah but with types you can do some operations on it
I prefer Interfaces simply because I don't have to write '=' when declaring one. As a developer, shaving 0.2 seconds is something I cannot take for granted.
there are definitely differences between types and interfaces, interface must have statically known properties while types don't, so you can have
type A = { [k in K]: O[K] }
you can't really define this type using interfaces, right? or am I missing something?
I think that's fine and it aligns with his advice. I think this video was for those ambiguous or subjective circumstances. In your case, it's absolutely clear you'd need an interface.
As someone who prefers Interfaces, I wish there is a way to either A) Mark a setting that interfaces are not mutable and disallow the default merging feature unless flagged inline, or B) Add a keyword inline to specifically disallow that behavior on that interface and in extension create naming collisions. Examples:
-------------------------------------------
Option A)
// tsconfig.json
{
...
"disallowInterfaceConsolidation": true
...
}
/*
* Because both "Interfaces" are prefaced by a "mut" keyword, they are mutable, unlike any other Interfaces
* that would otherwise throw an error on compliation saying they cannot be merged.
*/
mut Interface Animal {
sound: () => void;
}
mut Interface Animal {
furType: "feather" | "fur" | "bare"
}
-------------------------------------------
Option B)
// * Normal, current behavior
/*
* The code below would cause a TypeScript Error because Animal was set with an "immutable" keyword,
* but was declared twice at the same scope/level.
*
* Could also use "unique" as the keyword, maybe. Not sure what they keyword should be,
* just something to say it can't have a sibling level duplicate.
*/
immutable Interface Animal {
sound: () => void;
}
Interface Animal {
furType: "feather" | "fur" | "bare"
}
I tend to rarely use interfaces even though the extension syntax for interfaces is a bit cleaner, I usually just use the intersection operator. I've resolved to only use interfaces for implementing classes which I rarely do because I don't write a lot of classes (oop is the devil (class is still a good tool, just don't orient your entire program with them)).
I had switched to types but then switched back to interfaces since I had a Type named User and a React Component also named User. Now I have an interface named IUser and a React component named User.
I'm so glad you made this
I ran into this last year and kinda used both in the same project
I couldn't figure out what then difference was
But...
Everywhere you look says use an interface
Now I get it use Types in JavaScript / react the majority of times for safety reasons
Then use interfaces pretty much in every other language 😮😊
For anyone trying to dive deep into why a Type works but an interface won't, I quote one of the members of the TS teams from an issue of 2017 in this regard "this behavior is currently by design. Because interfaces can be augmented by additional declarations but type aliases can't, it's "safer" (heavy quotes on that one) to infer an implicit index signature for type aliases than for interfaces. But we'll consider doing it for interfaces as well if that seems to make sense"
I changed my mind three times just watching this video.
Coming from an OOP like C# and Java, an interface should be used when you want to define methods with no implementation and let the class inherit the interface and implement the methods, use type as the data object. That is how I see it.
Great video, thanks a lot for sharing, I really love the way you're explaining things :)
I generally end up using types for functional programming and interfaces for OOP. Or is it also better to use types when using classes?
I share your phase 3 view. I only use interfaces in very specific cases and types as default
Yes, haha I love the word interface, and that's why I use interfaces over types. Thanks for your video though, I wasn't even aware of these differences between them
I just found this amazing blog, so happy with its valuable content. I was wondering would u mind to prepare one about Hexagonal(Dependency Inversion) in TS? I come from OOP and I find TS really powerful but at times I miss the stability that brings Hexagonal and Service contracts in the form of Interfaces
Great piece of advice !!!
I just started today, have you changed again so I can get the latest recommended 😅
Type has four character, Interfaces has 10 character. what a obvoius choice! Type is always my first choice. I use interface when you cant use type. In my expirence in typescript I havnt encounter that situation.
I care about program speed, not speed of type checker. The type checker should be optimized as time goes by, but why should I care about the type checker when I already have a lot to care about by program?
Ok, now thanks to you, I'll have to convert all my interfaces to types again. Kiding 😄, good stuff!
I've had a conversation with GPT3 about this, and we came to the conclusion interfaces are better suited for defining contracts for classes, where types are better suited for defining contracts(shapes) for values; this would make interfaces a higher abstraction level than types. This completely ignores the quirks of interface merging and such, but what do you think of this?
that's funny. I just asked ChatGPT and it suggested using interfaces over types for both:
> "In summary, both interfaces and types can be used to define models in TypeScript, but interfaces are generally preferred for their simplicity, readability, and wider community adoption. Types are more flexible and can be used to define more complex type structures, but are typically used for more advanced type operations."
@@lamspamHow did you ask it?
@@RoyRope Here are my two questions before I got that response:
- In TypeScript, what is the best method of defining models or contracts?
- but what about Types?
@@lamspam ah oke interesting, sadly I asked GPT before the chat history existed. But I remember having a conversation asking about the pros en cons of types and interfaces and the different levels of abstaction inside a tyepscript program.
Overloading interfaces with function IO seems to preserve types better than alternative methods. At least where enums are used.
Seems we both came to the same conclusion. I used to use interfaces but I ran into werid instances where types just worked better and now I use types for everything unless I need a specific feature of interfaces.
Thank you! Wanted to know more about this and you explained it well! 🎉
Yup, that’s what I thought and use for years. Thanks for the video though!
Many thanks for content and insides!
So, I agree, "and" I would likely go a bit farther and say it would be best if you can simply use "types" all the time. Why? Well, basically because those things you mentioned Interfaces have that types don't are things that, IMHO, are things that should be avoided in JS as much as possible (for those who are about to argue that this is TS, not JS, TS gets transpiled to JS, and that's important to realize). I feel like just about everything that has gone wrong with JS started the very minute they tried to make it look more like JAVA (and that goes all the way back to the constructor function that was added to original JS). In my JS experience (which is over 25 years), every time I have tried to implement common OOP techniques like polymorphism and inheritance, very, very bad things eventually happened. The keyword there is eventually. The problems don't manifest right away. The main things that manifest early is slower code, and I could almost always point out a JAVA developer who was hating on JS because of its "slowness" because I knew they were doing it wrong. When ECMA capitulated and added the "class" keyword I thought, "here we go". That practically gave a license to use JS wrong. In short, composition and factory functions continue to be easier to maintain in the long run than inheritance (in JS/TS) and that's basically because JS is not object oriented. it's prototypal and the difference doesn't seem big until it does, but by then, there's not much you can do about it without lots of expense. It's a rarity when entire teams are so disciplined that their OOP code of JS doesn't get out of control (same with non-oop, actually, but many oop folks seem to think that they are magically protected from being undisciplined).
Also, there is one other benefit I've found to types that interfaces rarely have, and it has to do with the IDEs themselves. In my experience, many IDEs tend to give you more information about types than they do about interfaces when you hover over parameters, properties and variables that were declared with a type, vs an interface. Often, when it's an interface, all I will see is an interface name, but when it's a type, I'll often see the properties, restrictions, etc from that type. It's a subtle distinction and may depend on the IDE you use, but it makes a difference to me.
i like your conclusion
So I got asked on one of interviews - when should I use types over interfaces and is the difference?
I responded with almost same points but interviewer was not satisfied and responded that I can achieve exactly same things with types and intersections. In this video I was hoping for clarification on this one but seems like interviewer was just bullying me. Still don't get what he wanted from me.
It's not even neccessary for declaring that a class implements a type or that an interface extends from a type that the type being extended from is an interface. As long as the result is a plain object type, it can be extended from.
Would it be helpful(and solve the problem mentioned in the video) if we had some keyword to disallow interface extension? Something like "final" in Java? (Not meaning that you can't extend from the interface), but that you can't amend properties to existing interface.
It's pretty tough, because ALL projects have some kind of declaration merging. It's present in lib.dom.d.ts, and libraries which depend on it. It's a critical part of library code.
It's also pretty hard to lint for outside of TS, too.
So it's a tough problem to try and restrict it. I'd argue that a type _is_ a 'final' interface.
yeah, thinking about it there is probably a very minimal amount of places where this would be really helpful, and for all others just using Type would kind of do it.
Why would you want this though? Declaration merging is a feature of the language - you can do it with interfaces, functions, and probably other things as well. If you want to disallow declaration merging for a type then you can declare it as a `type`. If you're writing a library, please don't declare everything as `type` just to prevent declaration merging unless you're sure it's the right thing to do. I've used too many libraries that expose an `any` where they shouldn't, and there's little I can do about it downstream because they've opted for `type` everywhere.
We actually used to use abstract classes instead of interfaces in Typescript as they can also just be implemented and don't have this.
Well we also didn't use much experience with Typescript overall that point and today I would scrap that idea.
I have always preferred types because of the intersections.
I don't mean to be that guy but it would be nice at 1:21 if you just showed the actual correspondence between the typescript team and you instead of giving a "x is a kind of y" statement
Thank you