スキップしてメイン コンテンツに移動

[TypeScript] How to use "type"

Intro

When I write codes, I often confuse if I should use type alias or interface.
Actually, they are quite similar in some situations.

So I want to know how to distinct them.

Environments

  • TypeScript ver.3.8.3
  • Node.js ver.12.10.0

Similarity

After compiling

Both type alias and interface are only in TypeScript.
After compiling to JavaScript, they will be disappeared.

TypeScript


interface ISample{
    name: string;
}
type TSample = {
    message: string
};
function main() {
    const iSample: ISample = {
        name: 'Hello'
    };
    const tSample: TSample = {
        message: 'World'
    };
}
main();

JavaScript


"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
function main() {
    var iSample = {
        name: 'Hello'
    };
    var tSample = {
        message: 'World'
    };
}
main();

Treat as same type

If an object has same properties with them, they treat as their type, though the object doesn't implement them explicitly.

function main() {
    const iSample = {
        name: 'Hello'
    };
    const iSample2: ISample = iSample; // OK
    
    const tSample = {
        message: 'World'
    };
    const tSample2: TSample = tSample; // OK
}

Class implemmentation

Class can implement them.

/** OK */
class IClassSample implements ISample {
    public name: string = 'Hello'
}
/** OK */
class TClassSample implements TSample {
    public message: string = 'World'
}

Difference

Declaration merging

Interface has "declaration merging".
If there are some interfaces what have same name in same namespace, same file, and etc, they will be merged.

interface ISample{
    name: string;
}
interface ISample{
    message: string;
}
function main() {
    // 1st and 2nd ISample are merged.
    const iSample: ISample = {
        name: 'Hello',
        message: 'World'
    };
}

Extends

Only interface can extends type alias and interface.

interface ISample{
    name: string;
}
type TSample = {
    message: string
};
interface IExtendSample extends ISample, TSample {
    id: number;
}
function main() {
    const iSample: IExtendSample = {
        id: 0,
        name: 'Hello',
        message: 'World'
    };
}

But type alias can Cross type to get similar result.

interface ISample{
    name: string;
}
type TSample = {
    message: string
};
type TCrossSample = ISample & TSample & { id: number; }
function main() {
    const tSample: TCrossSample = {
        id: 0,
        name: 'Hello',
        message: 'World'
    };
}

Declare type

Interface is only declared in this way.

interface InterfaceName {
    propertyName: Type
};

But type alias can be declared in many kinds of type.


interface ISample{
    name: string;
}
type TSample = {
    message: string
};
/** Union type */
type TUnionSample = ISample|TSample;
/** Cross type */
type TCrossSample = ISample & TSample & { id: number; }
/** Tuple */
type TTupleSample = [string, number];
/** Function */
type TFunctionSample = (message: string) => void;
/** Object */
type TObjectSample = {
    message: string
};
/** Mapped type */
type TBase = {
    id: number,
    name: string
};
type TMappedSample = { [P in keyof TBase]: string; };
/** Conditional type */
type TConditionalSample<T> = T extends string? 'string': 'other';
function main() {
    const crossSample: TCrossSample = {
        id: 0,
        name: 'Hello',
        message: 'World'
    };
    const tupleSample: TTupleSample = ['hello', 0];
    const mappedSample: TMappedSample = {
        id: '0',
        name: 'Hello'
    };
    const conditionalSample: TConditionalSample<string> = 'string';
}

Inteface also can create some types like type alias.
For example, interface can create function type like item12 of "Effective TypeScript".

interface IFunctionSample {
    (message: string): void
}
type TFunctionSample = (message: string) => void;

const callIFunction: IFunctionSample = (message) => console.log(message);
const callTFunction: TFunctionSample = (message) => console.log(message);

function main() {
    callIFunction('Hello'); // OK
    callTFunction('World'); // OK
}

But the others that are like Union type etc. cannot.

When should I use "type"?

When I can use either, should I use type alias?
According to "Effective TypeScript", I should follow the project style.
And if I need to publish to other projects, I should choose interface.

When the project is for a web application, if the object is for detecting behaviour and implemented by class, I want to choose interface.
If the object is for saving some data like data transfer object, I want to choose type alias.
Maybe this is because I get used to write C# codes :)

References

TypeScript/spec.md at master · microsoft/TypeScript · GitHub
Advanced Types · TypeScript
Effective TypeScript
Interface vs Type alias in TypeScript 2.7 - Martin Hochel - Medium
TypeScript: Interfaces vs Types - Stack Overflow

コメント

このブログの人気の投稿

[Angular][ASP.NET Core] Upload chunked files

Intro I wanted to send files to Web application (made by ASP.NET Core). If the file size had been small, I didn't need do any special things. But when I tried to send a large file, the error was occurred by ASP.NET Core's limitation. Though I could change the settings, but I didn't want to do that, because I hadn't known the file sizes what would been actually using. So I splitted the data into chunks first, and sent them. After receiving all chunks, I merged them into one file. There might be some libraries or APIs (ex. Stream API) what did them automatically, but I couldn't find them. What I did [ASP.NET Core] Make CORS enabled [Angular] Split a large file into chunks [Angular][ASP.NET Core] Send and receive data as form data [ASP.NET Core] Merge chunks into one file [ASP.NET Core] Make CORS enabled Because the client side application(Angular) and the server side application(ASP.NET Core) had been separated, I had to make CORS(Cross-Origin Requests) ...

[Nest.js] Show static files

Intro I wanted to use Nest.js and WebRTC(node-webrtc). NestJS - A progressive Node.js framework Documentation | NestJS - A progressive Node.js framework And because I wanted to try with simple page(not use JavaScript frameworks), I added static HTML, CSS, JavaScript into a Nest.js project. Prepare Install First, I installed @nestjs/cli. First steps | NestJS - A progressive Node.js framework As same as last time , I couldn't do global install because I had used Volta. But I could installed by volta. volta install @nestjs/cli Create project nest new nest-web-rtc-sample volta pin node@12 Run npm start After doing "npm start", I could getting "Hello World!" from http://localhost:3000. Add static files I could add static files by two ways. @nestjs/serve-static First one of them was using "serve-static". Serve Static | NestJS - A progressive Node.js framework npm install --save @nestjs/serve-static And I needed adding a module into app.modu...

[Angular] Sending file with Observable and showing loading screen

Intro When I tried sending file data on last time, I had confused with "promise.then", "async/await" and "Observable". [Angular][ASP.NET Core] Upload chunked files So I wanted to distinct them, and this time, I tried to use "Observable" because HttpClient return Observable<any>. Call observables in order I sended file in these steps. Read file by FileReader Create directory for saving chunks send and saving chunks merge chunks to one file and delete chunks Each steps used the former steps result. So I could write by Promise.then like below. this.executeStep1() // return Promise<UploadResult> .then(result => this.executeStep2(result)) // return Promise<UploadResult> .then(result => this.executeStep3(result)) // return Promise<UploadResult> .catch(reason => console.log(reason)); Result I could write with pipe & flatMap. file-uploader.service.ts public upload(file: File): Observable<U...