ããã¯ããªã«ãããããŠæžãããã®ïŒ
TypeScriptã䜿ã£ãŠNode.jsã§ç°å¢å€æ°ãåå®çŸ©ããæ¹æ³ã調ã¹ããšãã ããã以äžã®ãããªèšè¿°ãèŠã€ããããã«æããŸãã
declare namespace NodeJS { interface ProcessEnv { readonly MY_ENV: string; } }
ããã§MY_ENV
ã¯å®çŸ©ãããç°å¢å€æ°åãšããŸãããã¡ããglobal.d.ts
ãšãã£ããã¡ã€ã«ãšããŠäœæããããšãã£ãæãã§ããã
ãªããšãªãé°å²æ°ã¯ãããã®ã§ãããçµæã ããæžãããã®ãããèŠã€ããã®ã§ããã¯ã©ãããããšãªã®ãã¡ãã£ãšèª¿ã¹ãŠã¿ãããšã«
ããŸããã
ç°å¢
ä»åã®ç°å¢ã¯ããã¡ãã
$ node --version v18.16.0 $ npm --version 9.5.1
TypeScriptïŒNode.jsãããžã§ã¯ããäœæãã
ãŸãã¯TypeScriptïŒNode.jsã®ãããžã§ã¯ããäœæããŸãã
$ npm init -y $ npm i -D typescript $ npm i -D prettier $ npm i -D jest @types/jest $ npm i -D esbuild esbuild-jest $ mkdir src test
TypeScriptããã¹ãçšã®Jestãªã©ãã€ã³ã¹ããŒã«ã
Node.jsã®åå®çŸ©æ å ±ãã€ã³ã¹ããŒã«ããŸãã
$ npm i -D @types/node@v18
è¿œå ããäŸåé¢ä¿ã®ããŒãžã§ã³ã¯ããã¡ãã
"devDependencies": { "@types/jest": "^29.5.2", "@types/node": "^18.16.18", "esbuild": "^0.18.4", "esbuild-jest": "^0.5.0", "jest": "^29.5.0", "prettier": "^2.8.8", "typescript": "^5.1.3" }
èšå®ãã¡ã€ã«ã¯ãtsconfig.json
ã ãå
ã«èŒããŠãããŸãã
tsconfig.json
{ "compilerOptions": { "target": "esnext", "module": "commonjs", "moduleResolution": "node", "lib": ["esnext"], "baseUrl": "./src", "outDir": "dist", "strict": true, "forceConsistentCasingInFileNames": true, "noFallthroughCasesInSwitch": true, "noImplicitOverride": true, "noImplicitReturns": true, "noPropertyAccessFromIndexSignature": true, "esModuleInterop": true }, "include": [ "src" ] }
ãã¹ãã§ã®åãã§ãã¯çšã
tsconfig.typecheck.json
{ "extends": "./tsconfig", "compilerOptions": { "baseUrl": "./", "noEmit": true }, "include": [ "src", "test" ] }
ãã®ä»ã¯ããŸãæåŸã«ã
ç°å¢å€æ°ãåç §ããTypeScriptãœãŒã¹ã³ãŒããæžã
ã§ã¯ããé¡ã«ããããã«TypeScriptïŒNode.jsã§ç°å¢å€æ°ãåç §ãããœãŒã¹ã³ãŒããæžããŠãããŸãããã
åç
§ããç°å¢å€æ°ã¯ãMY_ENV
ãšããŸãã
$ export MY_ENV=test
ãŸãã¯ãããªå®çŸ©ãæžããŠã¿ãŸãã
src/lookup-env.ts
export function lookupEnv(): string { return process.env.MY_ENV; }
ãã®ãœãŒã¹ã³ãŒãã¯ãã³ã³ãã€ã«ãšã©ãŒã«ãªããŸãã
$ npx tsc src/lookup-env.ts:4:3 - error TS2322: Type 'string | undefined' is not assignable to type 'string'. Type 'undefined' is not assignable to type 'string'. 4 return process.env.MY_ENV; ~~~~~~ src/lookup-env.ts:4:22 - error TS4111: Property 'MY_ENV' comes from an index signature, so it must be accessed with ['MY_ENV']. 4 return process.env.MY_ENV; ~~~~~~ Found 2 errors in the same file, starting at: src/lookup-env.ts:4
ä»æ¹ãªãã®ã§ãã²ãšãŸã以äžã®ããã«ãããšã³ã³ãã€ã«ãéãããã«ãªããŸãã
src/lookup-env.ts
export function lookupEnv(): string { return process.env['MY_ENV']!; // ãŸã㯠//return process.env['MY_ENV'] as string; }
ãã¹ãã³ãŒããæžããŠç¢ºèªããŸãããã
test/lookup-env.test.ts
import { lookupEnv } from '../src/lookup-env'; test('lookup environemt variable', () => { expect(lookupEnv()).toBe('test'); });
ãã®ãã¹ãã¯æåããŸãã
$ npm test > lookup-environmet-variable@1.0.0 test > jest PASS test/lookup-env.test.ts â lookup environemt variable (2 ms) Test Suites: 1 passed, 1 total Tests: 1 passed, 1 total Snapshots: 0 total Time: 0.195 s, estimated 2 s Ran all test suites.
ããã§ç°å¢å€æ°ã¯åç §ã§ããããã§ãããã§ããã°æåã«æžããããã«ä»¥äžã®ãããªèšè¿°ãããããã®ã§ãã
export function lookupEnv(): string { return process.env.MY_ENV; }
ããã§ã以äžã®ãããªTypeScriptãã¡ã€ã«ãäœæãããšprocess.env.MY_ENV
ãšããèšè¿°ã®ãŸãŸã³ã³ãã€ã«ãéãããšãã§ããŸãã
src/process.d.ts
declare namespace NodeJS { interface ProcessEnv { readonly MY_ENV: string; } }
ãã¹ããæåããŸãã
$ npm test > lookup-environmet-variable@1.0.0 test > jest PASS test/lookup-env.test.ts â lookup environemt variable (2 ms) Test Suites: 1 passed, 1 total Tests: 1 passed, 1 total Snapshots: 0 total Time: 0.268 s, estimated 1 s Ran all test suites.
ã§ãããã¯ã©ãããããšãªã®ããèŠãŠãããããšæããŸãã
以äžã®å®çŸ©ã«åãåã£ãŠããããšã«ãªããŸããã
declare namespace NodeJS { interface ProcessEnv { readonly MY_ENV: string; } }
çµè«ãèšããšãNode.jsã®å宣èšæ å ±ã§æžãããåå空éãšã€ã³ã¿ãŒãã§ãŒã¹ã®å®çŸ©ãšãã®èšè¿°ãããŒãžãããŠããããšã«ãªããŸãã
åå空é
ãŸãã以äžã®å®çŸ©ã¯åå空éã®å®£èšã§ãã
declare namespace NodeJS {
åå空éã«é¢ããããã¥ã¡ã³ãã¯ããã¡ãã
TypeScript: Documentation - Namespaces
åå空éã¯éå»ã«ãå éšã¢ãžã¥ãŒã«ããšåŒã°ããŠãããã®ã®ããã§ãTypeScriptç¬èªã®ãã®ã®ããã§ãã
åå空éã®èª¬æãèŠããšãååã®è¡çªãé¿ããããã®ä»çµã¿ãšãªã£ãŠããŸããã
weâre going to want to have some kind of organization scheme so that we can keep track of our types and not worry about name collisions with other objects.
管çäžãã¡ã€ã«å¥ã«åå²ããŠããæçµçã«ã¯çµ±åããŠäœ¿ãããšãæ³å®ããŠããããã§ãã
Once there are multiple files involved, weâll need to make sure all of the compiled code gets loaded.
Alternatively, we can use per-file compilation (the default) to emit one JavaScript file for each input file.
Namespaces / Multi-file namespaces
declare
ãšããã®ã¯ã¢ã³ããšã³ã宣èšãšãããã®ã§ãTypeScriptã³ã³ãã€ã©ãŒã«å®çŸ©æ
å ±ãäŒããããã«äœ¿ããã®ã®ããã§ãã
ãã®èšè¿°ããã¯JavaScriptã³ãŒãã¯çæããããå®è£
ãæã€ããšãã§ããŸããã
Namespaces / Ambient Namespaces
declare
ã¯åå空éã ãã§ã¯ãªããã¢ãžã¥ãŒã«ãé¢æ°ãå®æ°ïŒconst
ïŒãå€æ°ïŒlet
ãvar
ïŒã«å¯ŸããŠã䜿ããŸããããããã
å®çŸ©ã®ã¿ã§å®è£
ïŒåæå€ãïŒãæã€ããšã¯ã§ããŸããã
TypeScript: Documentation - Modules
åèïŒ
- アンビエント宣言(declare) - TypeScript Deep Dive 日本語版
- 型定義ファイル - TypeScript Deep Dive 日本語版
ãŸããå宣èšãã¡ã€ã«ïŒd.ts
ïŒã§ãããã¬ãã«ã«å®£èšãããã®ã¯declare
ãŸãã¯export
ã§éå§ããå¿
èŠãããããã§ãã
ããšãã°ãå çšã®å®çŸ©ã以äžã®ããã«å€æŽãããš
src/process.d.ts
namespace NodeJS { interface ProcessEnv { readonly MY_ENV: string; } }
ã³ã³ãã€ã«ãšã©ãŒã«ãªããŸãã
$ npx tsc src/process.d.ts:1:1 - error TS1046: Top-level declarations in .d.ts files must start with either a 'declare' or 'export' modifier. 1 namespace NodeJS { ~~~~~~~~~ Found 1 error in src/process.d.ts:1
åå空éãšã¢ãžã¥ãŒã«
åå空éãèŠãŠãããšããã¢ãžã¥ãŒã«ããšäŒŒããããªãã®ãªã®ã§ã¯ïŒãšæãã®ã§ãããéãã¯ãã¡ãã«æžãããŠããŸããã
TypeScript: Documentation - Namespaces and Modules
ã¡ãªã¿ã«ãTypeScriptã«ããããã¢ãžã¥ãŒã«ãã¯ãããã¬ãã«ã«import
ãexport
ãå«ããã®ãæãããã§ãã
In TypeScript, just as in ECMAScript 2015, any file containing a top-level import or export is considered a module.
TypeScript: Documentation - Modules
ãããã¬ãã«ã«import
ãexport
ã®å®£èšããªããã®ã¯ãã¹ã¯ãªããããšåŒã¶ããã§ãã
Conversely, a file without any top-level import or export declarations is treated as a script whose contents are available in the global scope (and therefore to modules as well).
åå空éãšã¢ãžã¥ãŒã«ã®éãã¯ã以äžã®èšè¿°ã端çãªããã«æããŸãã
Namespaces are simply named JavaScript objects in the global namespace.
Namespaces and Modules / Using Namespaces
åå空éã¯ãã°ããŒãã«ãªé åã®JavaScriptãªããžã§ã¯ãã«ååãã€ãããã®ããšããããšã§ãã
ãªããã¢ãžã¥ãŒã«ãšããŠæäŸããå Žåã«åå空éã¯äžèŠã ãšãããŠããŸãã
Namespaces and Modules / Needless Namespacing
ã¢ãžã¥ãŒã«ã®æç¹ã§ã¹ã³ãŒããç¬ç«ããŠããã®ã§ãããããããã«åå空éã䜿ã£ãŠãç ©éã«ãªãã ãããšããããšã®ããã§ãã
ã€ãŸããäžè¬çã«ã¯åå空éã§ã¯ãªãã¢ãžã¥ãŒã«ã䜿ãããšã«ãªããŸããåå空éã¯æ¢åã®ããã±ãŒãžã«å宣èšãè¡ãå Žåã«çãã
ãšãã£ã䜿ãæ¹ã«ãªãããã§ãã
宣èšã®ããŒãž
TypeScriptã§ã¯ãå¥ã ã®å Žæã«å®£èšãããå 容ãã²ãšã€ã«ããŒãžããæ©èœããããŸããããã宣èšã®ããŒãžãšåŒã¶ããã§ãã
TypeScript: Documentation - Declaration Merging
ããŒãžããã察象ã¯ã以äžã«ãªããŸãã
åå空éãã€ã³ã¿ãŒãã§ãŒã¹ãå«ãŸããŸãã
ããŒãžã®äŸã¯ãã¡ãã
// interface interface Cloner { clone(animal: Animal): Animal; } interface Cloner { clone(animal: Sheep): Sheep; } // interfaceïŒããŒãžåŸïŒ interface Cloner { clone(animal: Dog): Dog; clone(animal: Cat): Cat; } // namespace namespace Animals { export class Zebra {} } namespace Animals { export interface Legged { numberOfLegs: number; } export class Dog {} } // namespaceïŒããŒãžåŸïŒ namespace Animals { export interface Legged { numberOfLegs: number; } export class Zebra {} export class Dog {} }
ãšããããã§ã以äžã¯NodeJS
åå空éãšProcessEnv
ã€ã³ã¿ãŒãã§ãŒã¹ããå¥ã®å®£èšã«ããŒãžãããŠããããšã«ãªããŸãã
declare namespace NodeJS { interface ProcessEnv { readonly MY_ENV: string; } }
Node.jsã®åå®çŸ©ã§ã®å®£èšå Žæ
ã§ã¯ããã£ããã©ãã®å®£èšãšããŒãžãããŠããã®ããšããããã§ããã以äžã®ProcessEnv
ã€ã³ã¿ãŒãã§ãŒã¹ã§ããã
interface ProcessEnv extends Dict<string> { /** * Can be used to change the default timezone at runtime */ TZ?: string; }
ãããŠãProcessEnv
ã€ã³ã¿ãŒãã§ãŒã¹ã®å®çŸ©ã¯NodeJS
åå空éå
ã«å«ãŸããŠããŸãã
namespace NodeJS {
NodeJS
åå空éã¯ä»ã®å Žæã«ã宣èšããããNode.jsã®åå®çŸ©å
ã§ãNodeJS
åå空éãããŒãžãããŠããããšãããããŸããã
declare namespace NodeJS {
å®éã«åç §ããŠããå€æ°ã¯ããšãããšã
env
ã¯ãã¡ãã«å®çŸ©ããããŸãã
env: ProcessEnv;
ã§ã¯process
ã¯ãšãããšã以äžãããã«å®£èšãããŠããŸãã
global { var process: NodeJS.Process;
declare var process: NodeJS.Process;
ç¹ã«ãªã«ãimport
ããŠããªãã®ã«ãããªãprocess.env
ãšå
¥åããŠåã®æ
å ±ã解決ã§ããã®ã¯ãããã®å®£èšãããããã§ã
ä»åã¯ãã®å®£èšå
ã«èªåã§å®£èšããå
容ãããŒãžãããããšã«ãªããŸããã
ãšããããã§ããããè¿œããã®ã§ã¯ãªãããªãšæããŸãã
åèïŒ
グローバル変数の宣言 - TypeScript Deep Dive 日本語版
global.d.tsïŒglobals.d.tsïŒ
ãããã£ãæ
å ±ã調ã¹ãŠãããšãããglobal.d.ts
ïŒglobals.d.ts
ïŒã«å®çŸ©ãæžãããšãã£ãæ
å ±ãèŠãããã®ã§ããã
ãã®äŸãããã¥ã¡ã³ãã«æžãããŠããŸããã
TypeScript: Documentation - Global .d.ts
調ã¹ãŠãããšãã¡ã€ã«åã«ç¹å¥ãªæå³ããããã®ããã«ãèŠããã®ã§ããããã®æ å ±ã¯å ¬åŒããã¥ã¡ã³ãããã¯èŠã€ããããŸããã§ããâŠã
lib.d.ts - TypeScript Deep Dive 日本語版
global.d.ts - TypeScript Deep Dive 日本語版
ãã®ä»ã®ãã¡ã€ã«
ããšã¯ãä»åäœæããäž»èŠãªãã¡ã€ã«ãèŒããŠãããŸãã
src/lookup-env.ts
export function lookupEnv(): string { //return process.env['MY_ENV']!; //return process.env['MY_ENV'] as string; return process.env.MY_ENV; }
src/process.d.ts
declare namespace NodeJS { interface ProcessEnv { readonly MY_ENV: string; } }
test/lookup-env.test.ts
import { lookupEnv } from '../src/lookup-env'; test('lookup environemt variable', () => { expect(lookupEnv()).toBe('test'); });
package.json
ã
äŸåé¢ä¿ã
"devDependencies": { "@types/jest": "^29.5.2", "@types/node": "^18.16.18", "esbuild": "^0.18.4", "esbuild-jest": "^0.5.0", "jest": "^29.5.0", "prettier": "^2.8.8", "typescript": "^5.1.3" }
scripts
ã
"scripts": { "build": "tsc --project .", "build:watch": "tsc --project . --watch", "typecheck": "tsc --project ./tsconfig.typecheck.json", "typecheck:watch": "tsc --project ./tsconfig.typecheck.json --watch", "test": "jest", "format": "prettier --write src test" },
èšå®ãã¡ã€ã«ã
tsconfig.json
{ "compilerOptions": { "target": "esnext", "module": "commonjs", "moduleResolution": "node", "lib": ["esnext"], "baseUrl": "./src", "outDir": "dist", "strict": true, "forceConsistentCasingInFileNames": true, "noFallthroughCasesInSwitch": true, "noImplicitOverride": true, "noImplicitReturns": true, "noPropertyAccessFromIndexSignature": true, "esModuleInterop": true }, "include": [ "src" ] }
tsconfig.typecheck.json
{ "extends": "./tsconfig", "compilerOptions": { "baseUrl": "./", "noEmit": true }, "include": [ "src", "test" ] }
.prettierrc.json
{ "singleQuote": true, "printWidth": 120 }
jest.config.js
module.exports = { testEnvironment: 'node', transform: { "^.+\\.tsx?$": "esbuild-jest" } };
ãŸãšã
TypeScriptã䜿ã£ãŠNode.jsã®ç°å¢å€æ°ãåå®çŸ©ãããããšãè¡ã£ãŠã¿ã€ã€ãåå空éãšã¢ãžã¥ãŒã«ã®éãã宣èšã®å®£èšã®ããŒãžãšãã£ã
å
容ã確èªããŠã¿ãŸããã
æåã¯è»œãæ°æã¡ã§èª¿ã¹ãŠã¿ãããšæã£ãŠããã®ã§ããã調ã¹ãããšã次ã
ãšåºãŠããŠãŸã倧å€ã§ããããè¯ãåŠã³ã«ã¯ãªã£ãããªãš
æããŸãã
ããã¡ãã£ãšTypeScriptãšããèšèªèªäœãèŠãªããšãããªããªããšããæ°åã«ãªããŸããã