Typically one is more comfortable with interface if one's zone of familiarity is in terms of class-oriented object orientation, i.e. one predominantly thinks of objects in terms of "instances of a class".
type is more useful when you are typing "general data". Types aren't limited to "objects as structured data" but also include literal types. The type syntax also has a lot of features to pull information from JavaScript's value space into TypeScript's type space.
consttransform={A:'a',B:'b',C:'c',D:'d',E:'e',}asconst;typeTransform=typeoftransform;typeKey=keyofTransform;// type Key = "A" | "B" | "C" | "D" | "E"typeValue=Transform[Key];// type Value = "a" | "b" | "c" | "d" | "e" functionconvert(original:Key):Value{returntransform[original];}console.assert(convert('D')==='d','Value mismatch');
Above transform and convert exist in JavaScript's value space while Transform, Key and Value exist in TypeScripts's type space.
I love coding and what motivates me is problem-solving and preferably if it has an element of creativity.
I am a self-taught developer and work full-time as a front-end developer.
Location
Denmark 🇩🇰
Education
Bachelor in Nutrition and health (I know not super relevant to my current line of work!)
Wow appreciate your feedback. But, I think must of the stuff you described here is a bit beond my current skill level. I use typescript with React for now, and I have to admit I feel unsure why your above example wouldn't have worked equally well with "interface" instead of "types" 🤔
why your above example wouldn't have worked equally well with "interface" instead of "types" 🤔
Actually it's easier to explore when to use interface instead of type:
interface declarations merge, type aliases do not. So if it is necessary to declare an interface in bits-and-pieces interface is the only choice especially when monkey patching an existing class (which really should be avoided for built-in and even third party classes). With type each piece needs to be a separate type which are then combined by intersecting them.
By convention use interface not type when the declaration is going to be implemented by a class:
While syntactically correct
typeTitle={title:string;};classDogimplementsTitle{#title:string;constructor(breed:string,name:string){this.#title=`${name} (${breed})`;}gettitle():string{returnthis.#title;}}constfido=newDog('Maltese','Froufrou');constexpected='Froufrou (Maltese)';console.assert(fido.title===expected,`Title not "${expected}"`);
a class should implement an interface, not a type:
interfaceTitle{title:string;};classDogimplementsTitle{#title:string;constructor(breed:string,name:string){this.#title=`${name} (${breed})`;}gettitle():string{returnthis.#title;}}constfido=newDog('Maltese','Froufrou');constexpected='Froufrou (Maltese)';console.assert(fido.title===expected,`Title not "${expected}"`);
Everywhere else use type to get full access to TypeScript's typing features.
So for object types that are not class-based it makes sense to use type.
typeKey='A'|'B'|'C'|'D'|'E';// Can't do this with `interface` because `Key` isn't an object typetypeValue=1|2|3|4|5;typeEntry=[Key,Value];// Again a tuple isn't an object type - so `interface` is no help here// https://www.typescriptlang.org/docs/handbook/release-notes/typescript-1-3.html#tuple-types
Derived object types become type aliases, not interfaces:
constfido={breed:'Maltese',name:'Froufrou',title:()=>`${fido.name} (${fido.breed})`};typeDog=typeoffido;// type Dog = { breed: string, name: string, title: () => string }constexpected='Froufrou (Maltese)';console.assert(fido.title()===expected,`Title not "${expected}"`);
typeKeyName='name'|'breed'typeDog<T>={[keyinKeyName]:T;}{constname='Froufrou';constbreed='Maltese';// type inference:constdog={// const dog: { name: string, breed: string }name,breed};// type inference:constfido:Dog<string>={// const fido: Dog<string>name,breed};console.assert(name===dog.name&&breed===dog.breed,'dog: No match')console.assert(name===fido.name&&breed===fido.breed,'fido: No match')}
Because of the versatility of type aliases open source projects like comlink use them heavily - so being able to decipher type aliases can be helpful to understand the type constraints.
By limiting yourself to interface you aren't leveraging TypeScript's features as much as you could.
People usually get into type aliases once they realize how useful sum types (Union types) really are.
constleft:uniquesymbol=Symbol('Left');constright:uniquesymbol=Symbol('Right');// Discriminating Union + GenericstypeEither<L,R>=[typeofleft,L]|[typeofright,R];functionshowResult(result:Either<string,number>):void{switch(result[0]){caseleft:console.log('Error (Left):',result[1]);break;caseright:console.log('Success (Right):',result[1].toFixed(2));break;}}functionvalidate(value:number):Either<string,number>{returnvalue<10?[right,value]:[left,'Too Large'];}showResult(validate(Math.PI));// 'Success (Right): 3.14'showResult(validate(10));// 'Error (Left): Too Large'
Another nifty thing one can do with type:
// Only exists in the "type context"declareconstemailVerified:uniquesymbol;// Make structurally different from plain `string`typeEmail=string&{[emailVerified]:true}constverifiedEmails=newSet(['jane.doe@example.com']);// Assertion function// https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html#assertion-functionsfunctionassertIsEmail(email:string):assertsemailisEmail{if(!verifiedEmails.has(email))thrownewError(`"${email}" is not a verified email`);}// export this functionfunctionvalidateEmail(email:string):Email{assertIsEmail(email);// `email: string`returnemail;// `email: Email`}// Simple type aliastypeEmailAlias=string;try{constemail='jane.doe@example.com';constverified=validateEmail(email);// const verified: Emailconsole.log('Verified:',verified);// 'Verified: jane.doe@example.com'constanother='john.doe@example.net';constunverified:EmailAlias=another;// Simple type alias **will not** cause an error; however// const unverified: Email = another; // Type 'string' is not assignable to type 'Email'.// Type 'string' is not assignable to type '{ [emailVerified]: true; }'.(2322)constforced:Email=anotherasEmail;// However type assertion can silence the error on `Email` and narrow the type.// https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#type-assertionsconsole.log('Forced:',forced);// 'Forced: john.doe@example.net'constnotVerified=validateEmail(another);// Never gets hereconsole.log('Unverified',unverified);}catch(e){console.log(e.message);// "john.doe@example.net" is not a verified email}
In TypeScript's type contextEmail is structurally different from string - even though in JavaScript's value context it simply is a string.
This can help prevent a regular string from being assigned to Email without being validated (though a type assertion can force it) - without having to resort to a holder object:
typeEmail={email:string;}constverifiedEmails=newSet(['jane.doe@example.com']);functionvalidateEmail(email:string):Email{if(!verifiedEmails.has(email))thrownewError(`"${email}" is not a verified email`);return{email};}try{constemail='jane.doe@example.com';constverified=validateEmail(email);// const verified: Emailconsole.log('Verified:',verified.email);// 'Verified: jane.doe@example.com'constanother='john.doe@example.net';constforged:Email={email:another};console.log('Forged:',forged.email);// 'Forged: john.doe@example.net'constnotVerified=validateEmail(another);// never gets here}catch(e){console.log(e.message);// "john.doe@example.net" is not a verified email}
So types go beyond classes and interfaces. If you're not using type what are you using TypeScript for?
For further actions, you may consider blocking this person and/or reporting abuse
We're a place where coders share, stay up-to-date and grow their careers.
Typically one is more comfortable with
interface
if one's zone of familiarity is in terms of class-oriented object orientation, i.e. one predominantly thinks of objects in terms of "instances of a class".type
is more useful when you are typing "general data". Types aren't limited to "objects as structured data" but also include literal types. Thetype
syntax also has a lot of features to pull information from JavaScript's value space into TypeScript's type space.Above
transform
andconvert
exist in JavaScript's value space whileTransform
,Key
andValue
exist in TypeScripts's type space.One of the more interesting types are discriminated unions:
So from that perspective
type
is my default - unless for some reason I'm working with classes theninterface
is a better fit.Wow appreciate your feedback. But, I think must of the stuff you described here is a bit beond my current skill level. I use typescript with React for now, and I have to admit I feel unsure why your above example wouldn't have worked equally well with "interface" instead of "types" 🤔
Actually it's easier to explore when to use
interface
instead oftype
:interface
declarations merge,type
aliases do not. So if it is necessary to declare an interface in bits-and-piecesinterface
is the only choice especially when monkey patching an existingclass
(which really should be avoided for built-in and even third party classes). Withtype
each piece needs to be a separate type which are then combined by intersecting them.By convention use
interface
nottype
when the declaration is going to be implemented by aclass
:While syntactically correct
a
class
should implement aninterface
, not atype
:Everywhere else use
type
to get full access to TypeScript's typing features.So for object types that are not class-based it makes sense to use
type
.Derived object types become
type
aliases, notinterface
s:type
supports Mapped Types and Generics:Most of the Utility Types are defined via
type
aliases.Because of the versatility of
type
aliases open source projects like comlink use them heavily - so being able to decipher type aliases can be helpful to understand the type constraints.By limiting yourself to
interface
you aren't leveraging TypeScript's features as much as you could.People usually get into
type
aliases once they realize how useful sum types (Union types) really are.Another nifty thing one can do with
type
:In TypeScript's type context
Email
is structurally different fromstring
- even though in JavaScript's value context it simply is astring
.This can help prevent a regular
string
from being assigned toEmail
without being validated (though a type assertion can force it) - without having to resort to a holder object:So types go beyond classes and interfaces. If you're not using
type
what are you using TypeScript for?