Repository: gibbok/typescript-book Branch: main Commit: 16d6d2bc5306 Files: 374 Total size: 1.6 MB Directory structure: gitextract_xft80fat/ ├── .github/ │ └── workflows/ │ ├── compile.sh │ ├── lint.sh │ └── validate-content.yml ├── .gitignore ├── .vscode/ │ └── settings.json ├── FUNDING.yml ├── LICENSE.MD ├── README-it_IT.md ├── README-pt_BR.md ├── README-sv_SE.md ├── README-zh_CN.md ├── README.md ├── downloads/ │ ├── typescript-book-it_IT.epub │ ├── typescript-book-pt_BR.epub │ ├── typescript-book-sv_SE.epub │ ├── typescript-book-zh_CN.epub │ └── typescript-book.epub ├── tools/ │ ├── .markdownlint.json │ ├── .nvmrc │ ├── .prettierrc │ ├── .vscode/ │ │ ├── launch.json │ │ └── settings.json │ ├── Makefile │ ├── README.md │ ├── check_toc.py │ ├── compile.ts │ ├── config.ts │ ├── format.ts │ ├── i18n.ts │ ├── lint.ts │ ├── make-books.sh │ ├── make-website-content.py │ ├── package.json │ ├── remove_skip_empty_lines.sh │ ├── test-md/ │ │ ├── README-zh_CN.md │ │ └── README.md │ ├── tsconfig.json │ ├── utilities-add-translation/ │ │ ├── README.md │ │ ├── concatenate_language_files.py │ │ ├── create_language_files.py │ │ ├── split_markdown.py │ │ ├── verify_concatenation.py │ │ └── verify_concatenation_detailed.py │ ├── utils.ts │ └── verify_codeblocks.py └── website/ ├── .gitignore ├── .nvmrc ├── .prettierrc ├── .vscode/ │ ├── extensions.json │ └── launch.json ├── README.md ├── astro.config.mjs ├── package.json ├── public/ │ ├── cookie-banner/ │ │ ├── silktide-consent-manager.css │ │ └── silktide-consent-manager.js │ ├── cookie_policy.html │ └── google184c8848cff38265.html ├── src/ │ ├── components/ │ │ ├── Hero.astro │ │ └── RightSidebarBanner.astro │ ├── content/ │ │ ├── config.ts │ │ ├── docs/ │ │ │ ├── book/ │ │ │ │ ├── about-the-author.md │ │ │ │ ├── any-type.md │ │ │ │ ├── assignments.md │ │ │ │ ├── built-in-type-primitives.md │ │ │ │ ├── class.md │ │ │ │ ├── common-built-in-js-objects.md │ │ │ │ ├── conditional-types.md │ │ │ │ ├── control-flow-analysis.md │ │ │ │ ├── differences-between-type-and-interface.md │ │ │ │ ├── discriminated-unions.md │ │ │ │ ├── distributive-conditional-types.md │ │ │ │ ├── downloads-and-website.md │ │ │ │ ├── enums.md │ │ │ │ ├── erased-structural-types.md │ │ │ │ ├── exhaustiveness-checking.md │ │ │ │ ├── exploring-the-type-system.md │ │ │ │ ├── extending-types.md │ │ │ │ ├── fixed-length-tuple.md │ │ │ │ ├── generics.md │ │ │ │ ├── getting-started-with-typescript.md │ │ │ │ ├── index-signatures.md │ │ │ │ ├── infer-type-inference-in-conditional-types.md │ │ │ │ ├── interface-and-type.md │ │ │ │ ├── intersection-types.md │ │ │ │ ├── introduction.md │ │ │ │ ├── literal-inference.md │ │ │ │ ├── literal-types.md │ │ │ │ ├── mapped-type-modifiers.md │ │ │ │ ├── mapped-types.md │ │ │ │ ├── merging-and-extension.md │ │ │ │ ├── named-tuple-type-labeled.md │ │ │ │ ├── namespacing.md │ │ │ │ ├── narrowing.md │ │ │ │ ├── never-type.md │ │ │ │ ├── object-types.md │ │ │ │ ├── optional-properties.md │ │ │ │ ├── others.md │ │ │ │ ├── overloads.md │ │ │ │ ├── predefined-conditional-types.md │ │ │ │ ├── primitive-types.md │ │ │ │ ├── readonly-properties.md │ │ │ │ ├── strictnullchecks.md │ │ │ │ ├── symbols.md │ │ │ │ ├── table-of-contents.md │ │ │ │ ├── template-union-types.md │ │ │ │ ├── the-concise-typescript-book.md │ │ │ │ ├── the-never-type.md │ │ │ │ ├── translations.md │ │ │ │ ├── triple-slash-directives.md │ │ │ │ ├── tuple-type-anonymous.md │ │ │ │ ├── type-annotations.md │ │ │ │ ├── type-from-func-return.md │ │ │ │ ├── type-from-module.md │ │ │ │ ├── type-from-value.md │ │ │ │ ├── type-indexing.md │ │ │ │ ├── type-manipulation.md │ │ │ │ ├── type-predicates.md │ │ │ │ ├── typescript-introduction.md │ │ │ │ ├── union-type.md │ │ │ │ ├── unknown-type.md │ │ │ │ └── void-type.md │ │ │ ├── index.mdx │ │ │ ├── it-it/ │ │ │ │ ├── book/ │ │ │ │ │ ├── about-the-author.md │ │ │ │ │ ├── any-type.md │ │ │ │ │ ├── assignments.md │ │ │ │ │ ├── built-in-type-primitives.md │ │ │ │ │ ├── class.md │ │ │ │ │ ├── common-built-in-js-objects.md │ │ │ │ │ ├── conditional-types.md │ │ │ │ │ ├── control-flow-analysis.md │ │ │ │ │ ├── differences-between-type-and-interface.md │ │ │ │ │ ├── discriminated-unions.md │ │ │ │ │ ├── distributive-conditional-types.md │ │ │ │ │ ├── downloads-and-website.md │ │ │ │ │ ├── enums.md │ │ │ │ │ ├── erased-structural-types.md │ │ │ │ │ ├── exhaustiveness-checking.md │ │ │ │ │ ├── exploring-the-type-system.md │ │ │ │ │ ├── extending-types.md │ │ │ │ │ ├── fixed-length-tuple.md │ │ │ │ │ ├── generics.md │ │ │ │ │ ├── getting-started-with-typescript.md │ │ │ │ │ ├── index-signatures.md │ │ │ │ │ ├── infer-type-inference-in-conditional-types.md │ │ │ │ │ ├── interface-and-type.md │ │ │ │ │ ├── intersection-types.md │ │ │ │ │ ├── introduction.md │ │ │ │ │ ├── literal-inference.md │ │ │ │ │ ├── literal-types.md │ │ │ │ │ ├── mapped-type-modifiers.md │ │ │ │ │ ├── mapped-types.md │ │ │ │ │ ├── merging-and-extension.md │ │ │ │ │ ├── named-tuple-type-labeled.md │ │ │ │ │ ├── namespacing.md │ │ │ │ │ ├── narrowing.md │ │ │ │ │ ├── never-type.md │ │ │ │ │ ├── object-types.md │ │ │ │ │ ├── optional-properties.md │ │ │ │ │ ├── others.md │ │ │ │ │ ├── overloads.md │ │ │ │ │ ├── predefined-conditional-types.md │ │ │ │ │ ├── primitive-types.md │ │ │ │ │ ├── readonly-properties.md │ │ │ │ │ ├── strictnullchecks.md │ │ │ │ │ ├── symbols.md │ │ │ │ │ ├── table-of-contents.md │ │ │ │ │ ├── template-union-types.md │ │ │ │ │ ├── the-concise-typescript-book.md │ │ │ │ │ ├── the-never-type.md │ │ │ │ │ ├── translations.md │ │ │ │ │ ├── triple-slash-directives.md │ │ │ │ │ ├── tuple-type-anonymous.md │ │ │ │ │ ├── type-annotations.md │ │ │ │ │ ├── type-from-func-return.md │ │ │ │ │ ├── type-from-module.md │ │ │ │ │ ├── type-from-value.md │ │ │ │ │ ├── type-indexing.md │ │ │ │ │ ├── type-manipulation.md │ │ │ │ │ ├── type-predicates.md │ │ │ │ │ ├── typescript-introduction.md │ │ │ │ │ ├── union-type.md │ │ │ │ │ ├── unknown-type.md │ │ │ │ │ └── void-type.md │ │ │ │ └── index.mdx │ │ │ ├── pt-br/ │ │ │ │ ├── book/ │ │ │ │ │ ├── about-the-author.md │ │ │ │ │ ├── any-type.md │ │ │ │ │ ├── assignments.md │ │ │ │ │ ├── built-in-type-primitives.md │ │ │ │ │ ├── class.md │ │ │ │ │ ├── common-built-in-js-objects.md │ │ │ │ │ ├── conditional-types.md │ │ │ │ │ ├── control-flow-analysis.md │ │ │ │ │ ├── differences-between-type-and-interface.md │ │ │ │ │ ├── discriminated-unions.md │ │ │ │ │ ├── distributive-conditional-types.md │ │ │ │ │ ├── downloads-and-website.md │ │ │ │ │ ├── enums.md │ │ │ │ │ ├── erased-structural-types.md │ │ │ │ │ ├── exhaustiveness-checking.md │ │ │ │ │ ├── exploring-the-type-system.md │ │ │ │ │ ├── extending-types.md │ │ │ │ │ ├── fixed-length-tuple.md │ │ │ │ │ ├── generics.md │ │ │ │ │ ├── getting-started-with-typescript.md │ │ │ │ │ ├── index-signatures.md │ │ │ │ │ ├── infer-type-inference-in-conditional-types.md │ │ │ │ │ ├── interface-and-type.md │ │ │ │ │ ├── intersection-types.md │ │ │ │ │ ├── introduction.md │ │ │ │ │ ├── literal-inference.md │ │ │ │ │ ├── literal-types.md │ │ │ │ │ ├── mapped-type-modifiers.md │ │ │ │ │ ├── mapped-types.md │ │ │ │ │ ├── merging-and-extension.md │ │ │ │ │ ├── named-tuple-type-labeled.md │ │ │ │ │ ├── namespacing.md │ │ │ │ │ ├── narrowing.md │ │ │ │ │ ├── never-type.md │ │ │ │ │ ├── object-types.md │ │ │ │ │ ├── optional-properties.md │ │ │ │ │ ├── others.md │ │ │ │ │ ├── overloads.md │ │ │ │ │ ├── predefined-conditional-types.md │ │ │ │ │ ├── primitive-types.md │ │ │ │ │ ├── readonly-properties.md │ │ │ │ │ ├── strictnullchecks.md │ │ │ │ │ ├── symbols.md │ │ │ │ │ ├── table-of-contents.md │ │ │ │ │ ├── template-union-types.md │ │ │ │ │ ├── the-concise-typescript-book.md │ │ │ │ │ ├── the-never-type.md │ │ │ │ │ ├── translations.md │ │ │ │ │ ├── triple-slash-directives.md │ │ │ │ │ ├── tuple-type-anonymous.md │ │ │ │ │ ├── type-annotations.md │ │ │ │ │ ├── type-from-func-return.md │ │ │ │ │ ├── type-from-module.md │ │ │ │ │ ├── type-from-value.md │ │ │ │ │ ├── type-indexing.md │ │ │ │ │ ├── type-manipulation.md │ │ │ │ │ ├── type-predicates.md │ │ │ │ │ ├── typescript-introduction.md │ │ │ │ │ ├── union-type.md │ │ │ │ │ ├── unknown-type.md │ │ │ │ │ └── void-type.md │ │ │ │ └── index.mdx │ │ │ ├── sv-se/ │ │ │ │ ├── book/ │ │ │ │ │ ├── about-the-author.md │ │ │ │ │ ├── any-type.md │ │ │ │ │ ├── assignments.md │ │ │ │ │ ├── built-in-type-primitives.md │ │ │ │ │ ├── class.md │ │ │ │ │ ├── common-built-in-js-objects.md │ │ │ │ │ ├── conditional-types.md │ │ │ │ │ ├── control-flow-analysis.md │ │ │ │ │ ├── differences-between-type-and-interface.md │ │ │ │ │ ├── discriminated-unions.md │ │ │ │ │ ├── distributive-conditional-types.md │ │ │ │ │ ├── downloads-and-website.md │ │ │ │ │ ├── enums.md │ │ │ │ │ ├── erased-structural-types.md │ │ │ │ │ ├── exhaustiveness-checking.md │ │ │ │ │ ├── exploring-the-type-system.md │ │ │ │ │ ├── extending-types.md │ │ │ │ │ ├── fixed-length-tuple.md │ │ │ │ │ ├── generics.md │ │ │ │ │ ├── getting-started-with-typescript.md │ │ │ │ │ ├── index-signatures.md │ │ │ │ │ ├── infer-type-inference-in-conditional-types.md │ │ │ │ │ ├── interface-and-type.md │ │ │ │ │ ├── intersection-types.md │ │ │ │ │ ├── introduction.md │ │ │ │ │ ├── literal-inference.md │ │ │ │ │ ├── literal-types.md │ │ │ │ │ ├── mapped-type-modifiers.md │ │ │ │ │ ├── mapped-types.md │ │ │ │ │ ├── merging-and-extension.md │ │ │ │ │ ├── named-tuple-type-labeled.md │ │ │ │ │ ├── namespacing.md │ │ │ │ │ ├── narrowing.md │ │ │ │ │ ├── never-type.md │ │ │ │ │ ├── object-types.md │ │ │ │ │ ├── optional-properties.md │ │ │ │ │ ├── others.md │ │ │ │ │ ├── overloads.md │ │ │ │ │ ├── predefined-conditional-types.md │ │ │ │ │ ├── primitive-types.md │ │ │ │ │ ├── readonly-properties.md │ │ │ │ │ ├── strictnullchecks.md │ │ │ │ │ ├── symbols.md │ │ │ │ │ ├── table-of-contents.md │ │ │ │ │ ├── template-union-types.md │ │ │ │ │ ├── the-concise-typescript-book.md │ │ │ │ │ ├── the-never-type.md │ │ │ │ │ ├── translations.md │ │ │ │ │ ├── triple-slash-directives.md │ │ │ │ │ ├── tuple-type-anonymous.md │ │ │ │ │ ├── type-annotations.md │ │ │ │ │ ├── type-from-func-return.md │ │ │ │ │ ├── type-from-module.md │ │ │ │ │ ├── type-from-value.md │ │ │ │ │ ├── type-indexing.md │ │ │ │ │ ├── type-manipulation.md │ │ │ │ │ ├── type-predicates.md │ │ │ │ │ ├── typescript-introduction.md │ │ │ │ │ ├── union-type.md │ │ │ │ │ ├── unknown-type.md │ │ │ │ │ └── void-type.md │ │ │ │ └── index.mdx │ │ │ └── zh-cn/ │ │ │ ├── book/ │ │ │ │ ├── about-the-author.md │ │ │ │ ├── any-type.md │ │ │ │ ├── assignments.md │ │ │ │ ├── built-in-type-primitives.md │ │ │ │ ├── class.md │ │ │ │ ├── common-built-in-js-objects.md │ │ │ │ ├── conditional-types.md │ │ │ │ ├── control-flow-analysis.md │ │ │ │ ├── differences-between-type-and-interface.md │ │ │ │ ├── discriminated-unions.md │ │ │ │ ├── distributive-conditional-types.md │ │ │ │ ├── downloads-and-website.md │ │ │ │ ├── enums.md │ │ │ │ ├── erased-structural-types.md │ │ │ │ ├── exhaustiveness-checking.md │ │ │ │ ├── exploring-the-type-system.md │ │ │ │ ├── extending-types.md │ │ │ │ ├── fixed-length-tuple.md │ │ │ │ ├── generics.md │ │ │ │ ├── getting-started-with-typescript.md │ │ │ │ ├── index-signatures.md │ │ │ │ ├── infer-type-inference-in-conditional-types.md │ │ │ │ ├── interface-and-type.md │ │ │ │ ├── intersection-types.md │ │ │ │ ├── introduction.md │ │ │ │ ├── literal-inference.md │ │ │ │ ├── literal-types.md │ │ │ │ ├── mapped-type-modifiers.md │ │ │ │ ├── mapped-types.md │ │ │ │ ├── merging-and-extension.md │ │ │ │ ├── named-tuple-type-labeled.md │ │ │ │ ├── namespacing.md │ │ │ │ ├── narrowing.md │ │ │ │ ├── never-type.md │ │ │ │ ├── object-types.md │ │ │ │ ├── optional-properties.md │ │ │ │ ├── others.md │ │ │ │ ├── overloads.md │ │ │ │ ├── predefined-conditional-types.md │ │ │ │ ├── primitive-types.md │ │ │ │ ├── readonly-properties.md │ │ │ │ ├── strictnullchecks.md │ │ │ │ ├── symbols.md │ │ │ │ ├── table-of-contents.md │ │ │ │ ├── template-union-types.md │ │ │ │ ├── the-concise-typescript-book.md │ │ │ │ ├── the-never-type.md │ │ │ │ ├── translations.md │ │ │ │ ├── triple-slash-directives.md │ │ │ │ ├── tuple-type-anonymous.md │ │ │ │ ├── type-annotations.md │ │ │ │ ├── type-from-func-return.md │ │ │ │ ├── type-from-module.md │ │ │ │ ├── type-from-value.md │ │ │ │ ├── type-indexing.md │ │ │ │ ├── type-manipulation.md │ │ │ │ ├── type-predicates.md │ │ │ │ ├── typescript-introduction.md │ │ │ │ ├── union-type.md │ │ │ │ ├── unknown-type.md │ │ │ │ └── void-type.md │ │ │ └── index.mdx │ │ └── i18n/ │ │ └── zh-cn.json │ ├── env.d.ts │ └── styles/ │ └── custom.css └── tsconfig.json ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/workflows/compile.sh ================================================ #!/bin/bash set -e echo "Compiling all code snippets ..." cd tools npm ci npm run compile ================================================ FILE: .github/workflows/lint.sh ================================================ #!/bin/bash set -e echo "Linting Markdown and Code files ..." cd tools npm ci npm run lint npm run lint:md ================================================ FILE: .github/workflows/validate-content.yml ================================================ name: validate-content run-name: ${{ github.actor }} validate content on: [pull_request] jobs: lint-content: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Run lint script run: bash ${GITHUB_WORKSPACE}/.github/workflows/lint.sh compile-snippets: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Run compile script run: bash ${GITHUB_WORKSPACE}/.github/workflows/compile.sh ================================================ FILE: .gitignore ================================================ node_modules temp !.vscode __pycache__/ ================================================ FILE: .vscode/settings.json ================================================ { "editor.formatOnSave": false, } ================================================ FILE: FUNDING.yml ================================================ github: gibbok buy_me_a_coffee: simonepoggiali custom: ["https://www.paypal.com/donate/?business=QW82ZS956XLFY&no_recurring=0¤cy_code=EUR"] ================================================ FILE: LICENSE.MD ================================================ # Licence The Concise TypeScript Book © by Simone Poggiali is licensed under Attribution 4.0 International. For more information visit: ================================================ FILE: README-it_IT.md ================================================ # The Concise TypeScript Book The Concise TypeScript Book offre una panoramica completa e concisa delle funzionalità di TypeScript. Questo libro offre spiegazioni chiare che coprono tutti gli aspetti dell'ultima versione del linguaggio, dal suo potente sistema di tipi alle funzionalità avanzate. Che siate principianti o sviluppatori esperti, questo libro è una risorsa preziosa per migliorare la vostra comprensione e competenza in TypeScript. Questo libro è completamente gratuito e open source. Credo che un'istruzione tecnica di alta qualità debba essere accessibile a tutti, ed è per questo che mantengo questo libro gratuito e aperto. Se il libro ti ha aiutato a risolvere un bug, a comprendere un concetto ostico o a progredire nella tua carriera, ti prego di considerare di sostenere il mio lavoro pagando quanto vuoi (prezzo suggerito: 15 euro) o sponsorizzando un caffè. Il tuo supporto mi aiuta a mantenere i contenuti aggiornati e ad ampliarli con nuovi esempi e spiegazioni più approfondite. [![Buy Me a Coffee](https://img.shields.io/badge/buy_me_a_coffee-FFDD00?style=for-the-badge&logo=buy-me-a-coffee&logoColor=black)](https://www.buymeacoffee.com/simonepoggiali) [![Dona](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/donate/?business=QW82ZS956XLFY&no_recurring=0¤cy_code=EUR) ## Traduzioni Questo libro è stato tradotto in diverse lingue, tra cui: [Cinese](https://github.com/gibbok/typescript-book/blob/main/README-zh_CN.md) [Italiano](https://github.com/gibbok/typescript-book/blob/main/README-it_IT.md) [Portoghese (Brasile)](https://github.com/gibbok/typescript-book/blob/main/README-pt_BR.md) [Svedese](https://github.com/gibbok/typescript-book/blob/main/README-sv_SE.md) ## Download e sito web Puoi anche scaricare la versione Epub: [https://github.com/gibbok/typescript-book/tree/main/downloads](https://github.com/gibbok/typescript-book/tree/main/downloads) È disponibile una versione online su: [https://gibbok.github.io/typescript-book](https://gibbok.github.io/typescript-book) ## Indice - [The Concise TypeScript Book](#the-concise-typescript-book) - [Traduzioni](#traduzioni) - [Download e sito web](#download-e-sito-web) - [Indice](#indice) - [Introduzione](#introduzione) - [Informazioni sull'autore](#informazioni-sullautore) - [Introduzione a TypeScript](#introduzione-a-typescript) - [Cos'è TypeScript?](#cosè-typescript) - [Perché TypeScript?](#perché-typescript) - [TypeScript e JavaScript](#typescript-e-javascript) - [Generazione di codice TypeScript](#generazione-di-codice-typescript) - [JavaScript moderno ora (Downleveling)](#javascript-moderno-ora-downleveling) - [Per iniziare con TypeScript](#per-iniziare-con-typescript) - [Installazione](#installazione) - [Configurazione](#configurazione) - [File di configurazione TypeScript](#file-di-configurazione-typescript) - [target](#target) - [lib](#lib) - [strict](#strict) - [module](#module) - [moduleResolution](#moduleresolution) - [esModuleInterop](#esmoduleinterop) - [jsx](#jsx) - [skipLibCheck](#skiplibcheck) - [files](#files) - [include](#include) - [exclude](#exclude) - [importHelpers](#importhelpers) - [Consigli per la migrazione a TypeScript](#consigli-per-la-migrazione-a-typescript) - [Esplorazione del sistema di tipi](#esplorazione-del-sistema-di-tipi) - [Il servizio di linguaggio TypeScript](#il-servizio-di-linguaggio-typescript) - [Tipizzazione Strutturale](#tipizzazione-strutturale) - [Regole fondamentali di confronto di TypeScript](#regole-fondamentali-di-confronto-di-typescript) - [Tipi come insiemi](#tipi-come-insiemi) - [Allargamento di tipo](#allargamento-di-tipo) - [Const](#const) - [Modificatore Const sui parametri di tipo](#modificatore-const-sui-parametri-di-tipo) - [Asserzione Const](#asserzione-const) - [Annotazione di tipo esplicita](#annotazione-di-tipo-esplicita) - [Restringimento dei tipi](#restringimento-dei-tipi) - [Condizioni](#condizioni) - [Generazione o restituzione](#generazione-o-restituzione) - [Unione Discriminata](#unione-discriminata) - [Protezioni di tipo definite dall'utente](#protezioni-di-tipo-definite-dallutente) - [Tipi primitivi](#tipi-primitivi) - [string](#string) - [Inferenza letterale](#inferenza-letterale) - [strictNullChecks](#strictnullchecks) - [Enumerazioni](#enumerazioni) - [Enumerazioni numeriche](#enumerazioni-numeriche) - [Enum String](#enum-string) - [Enum Constant](#enum-constant) - [Mapping inverso](#mapping-inverso) - [Enum ambientali](#enum-ambientali) - [Membri calcolati e costanti](#membri-calcolati-e-costanti) - [Restringimento](#restringimento) - [protezioni di tipo typeof](#protezioni-di-tipo-typeof) - [Restringimento di veridicità](#restringimento-di-veridicità) - [Restringimento di uguaglianza](#restringimento-di-uguaglianza) - [Restringimento dell'operatore "in"](#restringimento-delloperatore-in) - [Restringimento instanceof](#restringimento-instanceof) - [Assegnazioni](#assegnazioni) - [Analisi del flusso di controllo](#analisi-del-flusso-di-controllo) - [Tipo da Valore](#tipo-da-valore) - [Tipo da Ritorno Funzione](#tipo-da-ritorno-funzione) - [Tipo da modulo](#tipo-da-modulo) - [Tipi mappati](#tipi-mappati) - [Modificatori di tipo mappati](#modificatori-di-tipo-mappati) - [Tipi condizionali](#tipi-condizionali) - [Tipi condizionali distributivi](#tipi-condizionali-distributivi) - [infer Inferenza di tipo nei tipi condizionali](#infer-inferenza-di-tipo-nei-tipi-condizionali) - [Tipi Condizionali Predefiniti](#tipi-condizionali-predefiniti) - [Tipi di unione di template](#tipi-di-unione-di-template) - [Tipo Any](#tipo-any) - [Tipo Unknown](#tipo-unknown) - [Tipo Void](#tipo-void) - [Tipo Never](#tipo-never) - [Interfaccia e tipo](#interfaccia-e-tipo) - [Sintassi comune](#sintassi-comune) - [Tipi di base](#tipi-di-base) - [Oggetti e interfacce](#oggetti-e-interfacce) - [Modificatori di accesso](#modificatori-di-accesso) - [Get e Set](#get-e-set) - [Accessori automatici nelle classi](#accessori-automatici-nelle-classi) - [this](#this) - [Proprietà dei parametri](#proprietà-dei-parametri) - [Classi astratte](#classi-astratte) - [Con i generici](#con-i-generici) - [Decoratori](#decoratori) - [Decoratori di classe](#decoratori-di-classe) - [Decoratore di proprietà](#decoratore-di-proprietà) - [Ereditarietà](#ereditarietà) - [Statiche](#statiche) - [Inizializzazione delle proprietà](#inizializzazione-delle-proprietà) - [Sovraccarico dei metodi](#sovraccarico-dei-metodi) - [Generici](#generici) - [Tipo generico](#tipo-generico) - [Classi generiche](#classi-generiche) - [Vincoli generici](#vincoli-generici) - [Restringimento contestuale generico](#restringimento-contestuale-generico) - [Tipi strutturali cancellati](#tipi-strutturali-cancellati) - [Namespace](#namespace) - [Simboli](#simboli) - [Direttive con tripla barra](#direttive-con-tripla-barra) - [Manipolazione dei tipi](#manipolazione-dei-tipi) - [Creazione di tipi da tipi](#creazione-di-tipi-da-tipi) - [Tipi di accesso indicizzati](#tipi-di-accesso-indicizzati) - [Tipi di utilità](#tipi-di-utilità) - [Awaited\](#awaitedt) - [Partial\](#partialt) - [Required\](#requiredt) - [Readonly\](#readonlyt) - [Record\](#recordk-t) - [Pick\](#pickt-k) - [Omit\](#omitt-k) - [Exclude\](#excludet-u) - [Extract\](#extractt-u) - [NonNullable\](#nonnullablet) - [Parameters\](#parameterst) - [ConstructorParameters\](#constructorparameterst) - [ReturnType\](#returntypet) - [InstanceType\](#instancetypet) - [ThisParameterType\](#thisparametertypet) - [OmitThisParameter\](#omitthisparametert) - [ThisType\](#thistypet) - [Uppercase\](#uppercaset) - [Lowercase\](#lowercaset) - [Capitalize\](#capitalizet) - [Uncapitalize\](#uncapitalizet) - [NoInfer\](#noinfert) - [Altri](#altri) - [Gestione degli errori e delle eccezioni](#gestione-degli-errori-e-delle-eccezioni) - [Classi Mixin](#classi-mixin) - [Funzionalità del linguaggio asincrono](#funzionalità-del-linguaggio-asincrono) - [Iteratori e Generatori](#iteratori-e-generatori) - [Riferimento JSDoc di TsDocs](#riferimento-jsdoc-di-tsdocs) - [@types](#types) - [JSX](#jsx-1) - [Moduli ES6](#moduli-es6) - [Operatore di elevamento a potenza ES7](#operatore-di-elevamento-a-potenza-es7) - [L'istruzione for-await-of](#listruzione-for-await-of) - [Nuova meta-proprietà target](#nuova-meta-proprietà-target) - [Espressioni di importazione dinamica](#espressioni-di-importazione-dinamica) - ["tsc –watch"](#tsc-watch) - [Operatore di asserzione non nullo](#operatore-di-asserzione-non-nullo) - [Dichiarazioni predefinite](#dichiarazioni-predefinite) - [Concatenamento opzionale](#concatenamento-opzionale) - [Operatore di coalescenza nullo](#operatore-di-coalescenza-nullo) - [Tipi letterali di template](#tipi-letterali-di-template) - [Sovraccarico di funzioni](#sovraccarico-di-funzioni) - [Tipi ricorsivi](#tipi-ricorsivi) - [Tipi condizionali ricorsivi](#tipi-condizionali-ricorsivi) - [Supporto per i moduli ECMAScript in Node](#supporto-per-i-moduli-ecmascript-in-node) - [Funzioni di asserzione](#funzioni-di-asserzione) - [Tipi di tupla variadici](#tipi-di-tupla-variadici) - [Tipi boxed](#tipi-boxed) - [Covarianza e Controvarianza in TypeScript](#covarianza-e-controvarianza-in-typescript) - [Annotazioni di varianza opzionali per i parametri di tipo](#annotazioni-di-varianza-opzionali-per-i-parametri-di-tipo) - [Firme di indice con pattern di stringhe modello](#firme-di-indice-con-pattern-di-stringhe-modello) - [Operatore `satisfies`](#operatore-satisfies) - [Importazioni ed esportazioni solo per tipo](#importazioni-ed-esportazioni-solo-per-tipo) - [Dichiarazione using e Gestione Risorse Esplicita](#dichiarazione-using-e-gestione-risorse-esplicita) - [dichiarazione await using](#dichiarazione-await-using) - [Attributi di importazione](#attributi-di-importazione) ## Introduzione Benvenuti a The Concise TypeScript Book! Questa guida vi fornirà le conoscenze essenziali e le competenze pratiche per uno sviluppo TypeScript efficace. Scoprite i concetti e le tecniche chiave per scrivere codice pulito e robusto. Che siate principianti o sviluppatori esperti, questo libro rappresenta sia una guida completa che un pratico riferimento per sfruttare la potenza di TypeScript nei vostri progetti. Questo libro tratta TypeScript 5.2. ## Informazioni sull'autore Simone Poggiali è uno Staff Engineer esperto, con una passione per la scrittura di codice di livello professionale fin dagli anni '90. Nel corso della sua carriera internazionale, ha contribuito a numerosi progetti per un'ampia gamma di clienti, dalle startup alle grandi organizzazioni. Aziende di spicco come HelloFresh, Siemens, O2, Leroy Merlin e Snowplow hanno beneficiato della sua competenza e dedizione. È possibile contattare Simone Poggiali sulle seguenti piattaforme: * LinkedIn: [https://www.linkedin.com/in/simone-poggiali](https://www.linkedin.com/in/simone-poggiali) * GitHub: [https://github.com/gibbok](https://github.com/gibbok) * X.com: [https://x.com/gibbok_coding](https://x.com/gibbok_coding) * Email: gibbok.coding📧gmail.com Elenco completo dei collaboratori: [https://github.com/gibbok/typescript-book/graphs/contributors](https://github.com/gibbok/typescript-book/graphs/contributors) ## Introduzione a TypeScript ### Cos'è TypeScript? TypeScript è un linguaggio di programmazione fortemente tipizzato basato su JavaScript. È stato originariamente progettato da Anders Hejlsberg nel 2012 ed è attualmente sviluppato e gestito da Microsoft come progetto open source. TypeScript si compila in JavaScript e può essere eseguito in qualsiasi runtime JavaScript (ad esempio, un browser o Node.js su un server). Supporta diversi paradigmi di programmazione, come la programmazione funzionale, generica, imperativa e orientata agli oggetti, ed è un linguaggio compilato (transpilato) che viene convertito in JavaScript prima dell'esecuzione. ### Perché TypeScript? TypeScript è un linguaggio fortemente tipizzato che aiuta a prevenire errori di programmazione comuni ed evitare determinati tipi di errori di runtime prima dell'esecuzione del programma. Un linguaggio fortemente tipizzato consente allo sviluppatore di specificare vari vincoli e comportamenti del programma nelle definizioni dei tipi di dati, facilitando la verifica della correttezza del software e la prevenzione dei difetti. Questo è particolarmente utile nelle applicazioni su larga scala. Alcuni dei vantaggi di TypeScript: * Tipizzazione statica, facoltativamente fortemente tipizzata * Inferenza di tipo * Accesso alle funzionalità di ES6 ed ES7 * Compatibilità multipiattaforma e multibrowser * Supporto degli strumenti con IntelliSense ### TypeScript e JavaScript TypeScript è scritto in file `.ts` o `.tsx`, mentre i file JavaScript sono scritti in file `.js` o `.jsx`. I file con estensione `.tsx` o `.jsx` possono contenere l'estensione di sintassi JavaScript JSX, utilizzata in React per lo sviluppo dell'interfaccia utente. TypeScript è un superset tipizzato di JavaScript (ECMAScript 2015) in termini di sintassi. Tutto il codice JavaScript è codice TypeScript valido, ma il contrario non è sempre vero. Ad esempio, si consideri una funzione in un file JavaScript con estensione `.js`, come la seguente: ```typescript const sum = (a, b) => a + b; ``` La funzione può essere convertita e utilizzata in TypeScript modificando l'estensione del file in `.ts`. Tuttavia, se la stessa funzione è annotata con tipi TypeScript, non può essere eseguita in alcun runtime JavaScript senza compilazione. Il seguente codice TypeScript genererà un errore di sintassi se non compilato: ```typescript const sum = (a: number, b: number): number => a + b; ``` TypeScript è stato progettato per rilevare possibili eccezioni che possono verificarsi in fase di runtime durante la compilazione, consentendo allo sviluppatore di definire l'intento con annotazioni di tipo. Inoltre, TypeScript può anche rilevare problemi se non viene fornita alcuna annotazione di tipo. Ad esempio, il seguente frammento di codice non specifica alcun tipo TypeScript: ```typescript const items = [{ x: 1 }, { x: 2 }]; const result = items.filter(item => item.y); ``` In questo caso, TypeScript rileva un errore e segnala: ```text La proprietà 'y' non esiste sul tipo '{ x: number; }'. ``` Il sistema di tipi di TypeScript è ampiamente influenzato dal comportamento runtime di JavaScript. Ad esempio, l'operatore di addizione (+), che in JavaScript può eseguire sia la concatenazione di stringhe che l'addizione numerica, è modellato allo stesso modo in TypeScript: ```typescript const result = '1' + 1; // Il risultato è di tipo stringa ``` Il team di TypeScript ha deliberatamente deciso di segnalare come errori l'utilizzo insolito di JavaScript. Ad esempio, si consideri il seguente codice JavaScript valido: ```typescript const result = 1 + true; // In JavaScript, il risultato è uguale a 2 ``` Tuttavia, TypeScript genera un errore: ```text L'operatore '+' non può essere applicato ai tipi 'number' e 'boolean'. ``` Questo errore si verifica perché TypeScript applica rigorosamente la compatibilità di tipo e, in questo caso, identifica un'operazione non valida tra un numero e un valore booleano. ### Generazione di codice TypeScript Il compilatore TypeScript ha due responsabilità principali: il controllo degli errori di tipo e la compilazione in JavaScript. Questi due processi sono indipendenti l'uno dall'altro. I tipi non influenzano l'esecuzione del codice in un runtime JavaScript, poiché vengono completamente cancellati durante la compilazione. TypeScript può comunque generare codice JavaScript anche in presenza di errori di tipo. Ecco un esempio di codice TypeScript con un errore di tipo: ```typescript const add = (a: number, b: number): number => a + b; const result = add('x', 'y'); // L'argomento di tipo 'string' non è assegnabile al parametro di tipo 'number'. ``` Tuttavia, può comunque produrre un output JavaScript eseguibile: ```typescript 'use strict'; const add = (a, b) => a + b; const result = add('x', 'y'); // xy ``` Non è possibile controllare i tipi TypeScript in fase di esecuzione. Ad esempio: ```typescript interface Animal { name: string; } interface Dog extends Animal { bark: () => void; } interface Cat extends Animal { meow: () => void; } const makeNoise = (animal: Animal) => { if (animal instanceof Dog) { // 'Dog' si riferisce solo a un tipo, ma qui viene utilizzato come valore. // ... } }; ``` Poiché i tipi vengono cancellati dopo la compilazione, non è possibile eseguire questo codice in JavaScript. Per riconoscere i tipi a runtime, dobbiamo usare un altro meccanismo. TypeScript offre diverse opzioni, una delle quali è la "tagged union". Ad esempio: ```typescript interface Dog { kind: 'dog'; // Tagged union bark: () => void; } interface Cat { kind: 'cat'; // Tagged union meow: () => void; } type Animal = Dog | Cat; const makeNoise = (animal: Animal) => { if (animal.kind === 'dog') { animal.bark(); } else { animal.meow(); } }; const dog: Dog = { kind: 'dog', bark: () => console.log('bark'), }; makeNoise(dog); ``` La proprietà "kind" è un valore che può essere utilizzato in fase di esecuzione per distinguere gli oggetti in JavaScript. È anche possibile che un valore in fase di esecuzione abbia un tipo diverso da quello dichiarato nella dichiarazione di tipo. Ad esempio, se lo sviluppatore ha interpretato erroneamente un tipo API e lo ha annotato in modo errato. TypeScript è un superset di JavaScript, quindi la parola chiave "class" può essere utilizzata come tipo e valore in fase di esecuzione. ```typescript class Animal { constructor(public name: string) {} } class Dog extends Animal { constructor( public name: string, public bark: () => void ) { super(name); } } class Cat extends Animal { constructor( public name: string, public meow: () => void ) { super(name); } } type Mammal = Dog | Cat; const makeNoise = (mammal: Mammal) => { if (mammal instanceof Dog) { mammal.bark(); } else { mammal.meow(); } }; const dog = new Dog('Fido', () => console.log('bark')); makeNoise(dog); ``` In JavaScript, una "classe" ha una proprietà "prototype" e l'operatore "instanceof" può essere utilizzato per verificare se la proprietà prototype di un costruttore appare in qualsiasi punto della catena di prototipi di un oggetto. TypeScript non ha alcun effetto sulle prestazioni di runtime, poiché tutti i tipi verranno cancellati. Tuttavia, TypeScript introduce un certo overhead in fase di compilazione. ### JavaScript moderno ora (Downleveling) TypeScript può compilare codice per qualsiasi versione rilasciata di JavaScript a partire da ECMAScript 3 (1999). Ciò significa che TypeScript può transpilare codice dalle funzionalità JavaScript più recenti a versioni precedenti, un processo noto come Downleveling. Questo consente l'utilizzo di JavaScript moderno mantenendo la massima compatibilità con gli ambienti di runtime più vecchi. È importante notare che durante la transpilazione a una versione precedente di JavaScript, TypeScript potrebbe generare codice che potrebbe comportare un sovraccarico di prestazioni rispetto alle implementazioni native. Ecco alcune delle funzionalità di JavaScript moderno che possono essere utilizzate in TypeScript: * Moduli ECMAScript al posto delle callback "define" in stile AMD o delle istruzioni "require" di CommonJS. * Classi al posto dei prototipi. * Dichiarazione di variabili utilizzando "let" o "const" al posto di "var". * Ciclo "for-of" o ".forEach" al posto del tradizionale ciclo "for". * Funzioni freccia al posto delle espressioni di funzione. * Assegnazione destrutturata. \* Nomi abbreviati di proprietà/metodi e nomi di proprietà calcolate. * Parametri di funzione predefiniti. Sfruttando queste moderne funzionalità di JavaScript, gli sviluppatori possono scrivere codice più espressivo e conciso in TypeScript. ## Per iniziare con TypeScript ### Installazione Visual Studio Code offre un eccellente supporto per il linguaggio TypeScript, ma non include il compilatore TypeScript. Per installare il compilatore TypeScript, è possibile utilizzare un gestore di pacchetti come npm o yarn: ```shell npm install typescript --save-dev ``` oppure ```shell yarn add typescript --dev ``` Assicurarsi di eseguire il commit del file di lock generato per garantire che ogni membro del team utilizzi la stessa versione di TypeScript. Per eseguire il compilatore TypeScript, è possibile utilizzare i seguenti comandi: ```shell npx tsc ``` oppure ```shell yarn tsc ``` Si consiglia di installare TypeScript a livello di progetto anziché globale, poiché garantisce un processo di build più prevedibile. Tuttavia, per occasioni particolari, è possibile utilizzare il seguente comando: ```shell npx tsc ``` oppure installarlo globalmente: ```shell npm install -g typescript ``` Se si utilizza Microsoft Visual Studio, è possibile ottenere TypeScript come pacchetto in NuGet per i progetti MSBuild. Nella console di Gestione Pacchetti di NuGet, eseguire il seguente comando: ```shell Install-Package Microsoft.TypeScript.MSBuild ``` Durante l'installazione di TypeScript, vengono installati due eseguibili: "tsc" come compilatore TypeScript e "tsserver" come server autonomo TypeScript. Il server autonomo contiene il compilatore e i servizi linguistici che possono essere utilizzati da editor e IDE per fornire il completamento intelligente del codice. Inoltre, sono disponibili diversi transpiler compatibili con TypeScript, come Babel (tramite un plugin) o swc. Questi transpiler possono essere utilizzati per convertire il codice TypeScript in altri linguaggi o versioni di destinazione. ### Configurazione TypeScript può essere configurato utilizzando le opzioni della CLI di tsc o un file di configurazione dedicato chiamato tsconfig.json, posizionato nella radice del progetto. Per generare un file tsconfig.json precompilato con le impostazioni consigliate, è possibile utilizzare il seguente comando: ```shell tsc --init ``` Quando si esegue il comando `tsc` localmente, TypeScript compilerà il codice utilizzando la configurazione specificata nel file tsconfig.json più vicino. Ecco alcuni esempi di comandi CLI che vengono eseguiti con le impostazioni predefinite: ```shell tsc main.ts // Compila un file specifico (main.ts) in JavaScript tsc src/*.ts // Compila tutti i file .ts nella cartella 'src' in JavaScript tsc app.ts util.ts --outfile index.js // Compila due file TypeScript (app.ts e util.ts) in un singolo file JavaScript (index.js) ``` ### File di configurazione TypeScript Un file tsconfig.json viene utilizzato per configurare il compilatore TypeScript (tsc). Solitamente, viene aggiunto alla radice del progetto, insieme al file `package.json`. Note: * tsconfig.json accetta commenti anche se è in formato json. * Si consiglia di utilizzare questo file di configurazione al posto delle opzioni della riga di comando. Al seguente link potete trovare la documentazione completa e il relativo schema: [https://www.typescriptlang.org/tsconfig](https://www.typescriptlang.org/tsconfig) [https://www.typescriptlang.org/tsconfig/](https://www.typescriptlang.org/tsconfig/) Di seguito è riportato un elenco delle configurazioni più comuni e utili: #### target La proprietà "target" viene utilizzata per specificare in quale versione di JavaScript ECMAScript TypeScript deve emettere/compilare. Per i browser moderni, ES6 è una buona opzione, mentre per i browser più vecchi si consiglia ES5. #### lib La proprietà "lib" viene utilizzata per specificare quali file di libreria includere in fase di compilazione. TypeScript include automaticamente le API per le funzionalità specificate nella proprietà "target", ma è possibile omettere o selezionare librerie specifiche per esigenze particolari. Ad esempio, se si lavora su un progetto server, è possibile escludere la libreria "DOM", utile solo in un ambiente browser. #### strict La proprietà "strict" offre garanzie più solide e migliora la sicurezza dei tipi. Si consiglia di includere sempre questa proprietà nel file tsconfig.json del progetto. Abilitando la proprietà "strict", TypeScript può: * Emettere codice utilizzando "use strict" per ogni file sorgente. * Considerare "null" e "undefined" nel processo di controllo dei tipi. * Disabilitare l'utilizzo del tipo "any" quando non sono presenti annotazioni di tipo. * Generare un errore sull'utilizzo dell'espressione "this", che altrimenti implicherebbe il tipo "any". #### module La proprietà "module" imposta il sistema di moduli supportato dal programma compilato. Durante l'esecuzione, un caricatore di moduli viene utilizzato per individuare ed eseguire le dipendenze in base al sistema di moduli specificato. I caricatori di moduli più comuni utilizzati in JavaScript sono Node.js CommonJS per le applicazioni lato server e RequireJS per i moduli AMD nelle applicazioni web basate su browser. TypeScript può generare codice per vari sistemi di moduli, tra cui UMD, System, ESNext, ES2015/ES6 ed ES2020. Nota: il sistema di moduli deve essere scelto in base all'ambiente di destinazione e al meccanismo di caricamento dei moduli disponibile in tale ambiente. #### moduleResolution La proprietà "moduleResolution" specifica la strategia di risoluzione dei moduli. Utilizzare "node" per il codice TypeScript moderno, la strategia "classic" viene utilizzata solo per le vecchie versioni di TypeScript (precedenti alla 1.6). #### esModuleInterop La proprietà "esModuleInterop" consente l'importazione predefinita dai moduli CommonJS che non sono stati esportati utilizzando la proprietà "default". Questa proprietà fornisce uno shim per garantire la compatibilità nel codice JavaScript emesso. Dopo aver abilitato questa opzione, possiamo usare `import MyLibrary from "my-library"` invece di `import * as MyLibrary from "my-library"`. #### jsx La proprietà "jsx" si applica solo ai file .tsx utilizzati in ReactJS e controlla il modo in cui i costrutti JSX vengono compilati in JavaScript. Un'opzione comune è "preserve", che compilerà in un file .jsx mantenendo invariato il codice JSX, in modo che possa essere passato a diversi strumenti come Babel per ulteriori trasformazioni. #### skipLibCheck La proprietà "skipLibCheck" impedisce a TypeScript di controllare il tipo di tutti i pacchetti di terze parti importati. Questa proprietà riduce il tempo di compilazione di un progetto. TypeScript controllerà comunque il codice rispetto alle definizioni di tipo fornite da questi pacchetti. #### files La proprietà "files" indica al compilatore un elenco di file che devono essere sempre inclusi nel programma. #### include La proprietà "include" indica al compilatore un elenco di file che si desidera includere. Questa proprietà consente schemi di tipo glob, come "\*_" per qualsiasi sottodirectory, "_" per qualsiasi nome di file e "?" per caratteri opzionali. #### exclude La proprietà "exclude" indica al compilatore un elenco di file che non devono essere inclusi nella compilazione. Questo può includere file come "node_modules" o file di test. Nota: tsconfig.json consente commenti. ### importHelpers TypeScript utilizza codice helper durante la generazione di codice per determinate funzionalità JavaScript avanzate o di livello inferiore. Per impostazione predefinita, questi helper vengono duplicati nei file che li utilizzano. L'opzione `importHelpers` importa invece questi helper dal modulo `tslib`, rendendo l'output JavaScript più efficiente. ### Consigli per la migrazione a TypeScript Per progetti di grandi dimensioni, si consiglia di adottare una transizione graduale in cui TypeScript e codice JavaScript coesisteranno inizialmente. Solo i progetti di piccole dimensioni possono essere migrati a TypeScript in un'unica soluzione. Il primo passo di questa transizione è introdurre TypeScript nel processo di build chain. Questo può essere fatto utilizzando l'opzione del compilatore "allowJs", che consente ai file .ts e .tsx di coesistere con i file JavaScript esistenti. Poiché TypeScript tornerà al tipo "any" per una variabile quando non riesce a dedurre il tipo dai file JavaScript, si consiglia di disabilitare "noImplicitAny" nelle opzioni del compilatore all'inizio della migrazione. Il secondo passaggio consiste nell'assicurarsi che i test JavaScript funzionino insieme ai file TypeScript, in modo da poterli eseguire durante la conversione di ciascun modulo. Se si utilizza Jest, si può valutare l'utilizzo di `ts-jest`, che consente di testare i progetti TypeScript con Jest. Il terzo passaggio consiste nell'includere le dichiarazioni di tipo per le librerie di terze parti nel progetto. Queste dichiarazioni sono disponibili in bundle o su DefinitelyTyped. È possibile cercarle utilizzando [https://www.typescriptlang.org/dt/search](https://www.typescriptlang.org/dt/search) e installarle tramite: ```shell npm install --save-dev @types/package-name ``` or ```shell yarn add --dev @types/package-name ``` Il quarto passaggio consiste nel migrare modulo per modulo con un approccio bottom-up, seguendo il grafo delle dipendenze partendo dalle foglie. L'idea è di iniziare a convertire i moduli che non dipendono da altri moduli. Per visualizzare i grafici delle dipendenze, è possibile utilizzare lo strumento "madge". I moduli candidati ideali per queste conversioni iniziali sono funzioni di utilità e codice relativo ad API o specifiche esterne. È possibile generare automaticamente definizioni di tipo TypeScript da contratti Swagger, schemi GraphQL o JSON da includere nel progetto. Quando non sono disponibili specifiche o schemi ufficiali, è possibile generare tipi da dati grezzi, come JSON restituiti da un server. Tuttavia, si consiglia di generare tipi da specifiche anziché da dati per evitare di perdere casi limite. Durante la migrazione, evitare il refactoring del codice e concentrarsi solo sull'aggiunta di tipi ai moduli. Il quinto passaggio consiste nell'abilitare "noImplicitAny", che garantirà che tutti i tipi siano noti e definiti, offrendo una migliore esperienza TypeScript per il progetto. Durante la migrazione, è possibile utilizzare la direttiva `@ts-check`, che abilita il controllo dei tipi TypeScript in un file JavaScript. Questa direttiva fornisce una versione semplificata del controllo dei tipi e può essere utilizzata inizialmente per identificare problemi nei file JavaScript. Quando `@ts-check` è incluso in un file, TypeScript tenterà di dedurre le definizioni utilizzando commenti in stile JSDoc. Tuttavia, si consiglia di utilizzare le annotazioni JSDoc solo in una fase molto precoce della migrazione. Si consiglia di mantenere il valore predefinito di `noEmitOnError` nel file tsconfig.json su false. Questo consentirà di generare codice sorgente JavaScript anche se vengono segnalati errori. ## Esplorazione del sistema di tipi ### Il servizio di linguaggio TypeScript Il servizio di linguaggio TypeScript, noto anche come tsserver, offre diverse funzionalità come la segnalazione degli errori, la diagnostica, la compilazione al salvataggio, la ridenominazione, il passaggio alla definizione, gli elenchi di completamento, la guida alle firme e altro ancora. Viene utilizzato principalmente dagli ambienti di sviluppo integrati (IDE) per fornire supporto IntelliSense. Si integra perfettamente con Visual Studio Code ed è utilizzato da strumenti come Conquer of Completion (Coc). Gli sviluppatori possono sfruttare un'API dedicata e creare plugin di servizi linguistici personalizzati per migliorare l'esperienza di modifica di TypeScript. Questo può essere particolarmente utile per implementare funzionalità di linting speciali o abilitare il completamento automatico per un linguaggio di template personalizzato. Un esempio di plugin personalizzato reale è "TypeScript-styled-plugin", che fornisce la segnalazione degli errori di sintassi e il supporto IntelliSense per le proprietà CSS nei componenti con stile. Per ulteriori informazioni e guide rapide, è possibile consultare il Wiki ufficiale di TypeScript su GitHub: [https://github.com/microsoft/TypeScript/wiki/](https://github.com/microsoft/TypeScript/wiki/) ### Tipizzazione Strutturale TypeScript si basa su un sistema di tipi strutturale. Ciò significa che la compatibilità e l'equivalenza dei tipi sono determinate dalla struttura o definizione effettiva del tipo, piuttosto che dal suo nome o dal punto di dichiarazione, come nei sistemi di tipi nominativi come C# o C. Il sistema di tipi strutturale di TypeScript è stato progettato sulla base del funzionamento del sistema di tipizzazione dinamica di JavaScript durante l'esecuzione. L'esempio seguente è codice TypeScript valido. Come si può osservare, "X" e "Y" hanno lo stesso membro "a", anche se hanno nomi di dichiarazione diversi. I tipi sono determinati dalle loro strutture e, in questo caso, poiché le strutture sono le stesse, sono compatibili e validi. ```typescript type X = { a: string; }; type Y = { a: string; }; const x: X = { a: 'a' }; const y: Y = x; // Valido ``` ### Regole fondamentali di confronto di TypeScript Il processo di confronto di TypeScript è ricorsivo ed è eseguito su tipi annidati a qualsiasi livello. Un tipo "X" è compatibile con "Y" se "Y" ha almeno gli stessi membri di "X". ```typescript type X = { a: string; }; const y = { a: 'A', b: 'B' }; // Valido, poiché ha almeno gli stessi membri di X const r: X = y; ``` I parametri delle funzioni vengono confrontati in base al tipo, non al nome: ```typescript type X = (a: number) => void; type Y = (a: number) => void; let x: X = (j: number) => undefined; let y: Y = (k: number) => undefined; y = x; // Valido x = y; // Valido ``` I tipi restituiti dalla funzione devono essere gli stessi: ```typescript type X = (a: number) => undefined; type Y = (a: number) => number; let x: X = (a: number) => undefined; let y: Y = (a: number) => 1; y = x; // Non valido x = y; // Non valido ``` Il tipo di ritorno di una funzione sorgente deve essere un sottotipo del tipo di ritorno di una funzione target: ```typescript let x = () => ({ a: 'A' }); let y = () => ({ a: 'A', b: 'B' }); x = y; // Valido y = x; // Il membro non valido b è mancante ``` È consentito ignorare i parametri della funzione, come è prassi comune in JavaScript, ad esempio utilizzando "Array.prototype.map()": ```typescript [1, 2, 3].map((element, _index, _array) => element + 'x'); ``` Pertanto, le seguenti dichiarazioni di tipo sono completamente valide: ```typescript type X = (a: number) => undefined; type Y = (a: number, b: number) => undefined; let x: X = (a: number) => undefined; let y: Y = (a: number) => undefined; // Parametro b mancante y = x; // Valido ``` Tutti i parametri opzionali aggiuntivi del tipo sorgente sono validi: ```typescript type X = (a: number, b?: number, c?: number) => undefined; type Y = (a: number) => undefined; let x: X = a => undefined; let y: Y = a => undefined; y = x; // Valido x = y; //Valido ``` Tutti i parametri opzionali del tipo destinazione senza parametri corrispondenti nel tipo sorgente sono validi e non costituiscono un errore: ```typescript type X = (a: number) => undefined; type Y = (a: number, b?: number) => undefined; let x: X = a => undefined; let y: Y = a => undefined; y = x; // Valido x = y; // Valido ``` Il parametro rest viene trattato come una serie infinita di parametri opzionali: ```typescript type X = (a: number, ...rest: number[]) => undefined; let x: X = a => undefined; //valido ``` Le funzioni con overload sono valide se la firma di overload è compatibile con la firma della sua implementazione: ```typescript function x(a: string): void; function x(a: string, b: number): void; function x(a: string, b?: number): void { console.log(a, b); } x('a'); // Valido x('a', 1); // Valido function y(a: string): void; // Non valido, non compatibile con la firma dell'implementazione function y(a: string, b: number): void; function y(a: string, b: number): void { console.log(a, b); } y('a'); y('a', 1); ``` Il confronto dei parametri della funzione ha esito positivo se i parametri sorgente e destinazione sono assegnabili a supertipi o sottotipi (bivarianza). ```typescript // Supertipo class X { a: string; constructor(value: string) { this.a = value; } } // Sottotipo class Y extends X {} // Sottotipo class Z extends X {} type GetA = (x: X) => string; const getA: GetA = x => x.a; // La bivarianza accetta supertipi console.log(getA(new X('x'))); // Valido console.log(getA(new Y('Y'))); // Valido console.log(getA(new Z('z'))); // Valido ``` Gli enum sono confrontabili e validi con i numeri e viceversa, ma il confronto di valori Enum di tipi Enum diversi non è valido. ```typescript enum X { A, B, } enum Y { A, B, C, } const xa: number = X.A; // Valido const ya: Y = 0; // Valido X.A === Y.A; // Non valido ``` Le istanze di una classe sono soggette a un controllo di compatibilità per i loro membri privati ​​e protetti: ```typescript class X { public a: string; constructor(value: string) { this.a = value; } } class Y { private a: string; constructor(value: string) { this.a = value; } } let x: X = new Y('y'); // Non valido ``` Il controllo di confronto non tiene conto della diversa gerarchia di ereditarietà, ad esempio: ```typescript class X { public a: string; constructor(value: string) { this.a = value; } } class Y extends X { public a: string; constructor(value: string) { super(value); this.a = value; } } class Z { public a: string; constructor(value: string) { this.a = value; } } let x: X = new X('x'); let y: Y = new Y('y'); let z: Z = new Z('z'); x === y; // Valido x === z; // Valido anche se z proviene da una gerarchia di ereditarietà diversa ``` I generici vengono confrontati utilizzando le loro strutture in base al tipo risultante dopo l'applicazione del parametro generico; solo il risultato finale viene confrontato come tipo non generico. ```typescript interface X { a: T; } let x: X = { a: 1 }; let y: X = { a: 'a' }; x === y; // Non valido poiché l'argomento tipo è utilizzato nella struttura finale ``` ```typescript interface X {} const x: X = 1; const y: X = 'a'; x === y; // Valido poiché l'argomento tipo non è utilizzato nella struttura finale ``` Quando i generici non hanno il loro argomento tipo specificato, tutti gli argomenti non specificati vengono trattati come tipi con "any": ```typescript type X = (x: T) => T; type Y = (y: K) => K; let x: X = x => x; let y: Y = y => y; x = y; // Valido ``` Ricorda: ```typescript let a: number = 1; let b: number = 2; a = b; // Valido, tutto è assegnabile a se stesso let c: any; c = 1; // Valido, tutti i tipi sono assegnabili a qualsiasi let d: unknown; d = 1; // Valido, tutti i tipi sono assegnabili a sconosciuto let e: unknown; let e1: unknown = e; // Valido, sconosciuto è assegnabile solo a se stesso e a qualsiasi let e2: any = e; // Valido let e3: number = e; // Non valido let f: never; f = 1; // Non valido, nulla è assegnabile a never let g: void; let g1: any; g = 1; // Non valido, void non è assegnabile a o da nulla, tranne qualsiasi g = g1; // Valido ``` Si noti che quando "strictNullChecks" è abilitato, "null" e "undefined" vengono trattati in modo simile a "void"; in caso contrario, sono simili a "never". ### Tipi come insiemi In TypeScript, un tipo è un insieme di possibili valori. Questo insieme è anche definito dominio del tipo. Ogni valore di un tipo può essere visto come un elemento di un insieme. Un tipo stabilisce i vincoli che ogni elemento dell'insieme deve soddisfare per essere considerato membro di quell'insieme. Il compito principale di TypeScript è controllare e verificare se un insieme è un sottoinsieme di un altro. TypeScript supporta vari tipi di insiemi: | Termine di insieme | TypeScript | Note | | ------------------------------ | --------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | | Insieme vuoto | never | "never" non contiene nulla | | Insieme di un singolo elemento | undefined / null / tipo letterale | | | Insieme finito | boolean / union | | | Insieme infinito | string / number / object | | | Insieme universale | any / unknown | Ogni elemento è un membro di "any" e ogni insieme è un suo sottoinsieme / "unknown" è una controparte di tipo sicuro di "any" | Ecco alcuni esempi: | TypeScript | Termine di insieme | Esempio | | -------------- | --------------------------- | --------------------------------------------------------------------------------- | | never | ∅ (insieme vuoto) | const x: never = 'x'; // Errore: il tipo 'string' non è assegnabile al tipo 'never' | | | | | Tipo letterale | Insieme di elementi singoli | type X = 'X'; | | | | type Y = 7; | | | | | Valore assegnabile a T | Valore ∈ T (membro di) | type XY = 'X' \| 'Y'; | | | | const x: XY = 'X'; | | | | | T1 assegnabile a T2 | T1 ⊆ T2 (sottoinsieme di) | type XY = 'X' \| 'Y'; | | | | const x: XY = 'X'; | | | | const j: XY = 'J'; // Il tipo '"J"' non è assegnabile al tipo 'XY'. | | | | | | T1 extends T2 | T1 ⊆ T2 (sottoinsieme di) | type X = 'X' extends string ? true : false; | | | | | T1 \| T2 | T1 ∪ T2 (unione) | type XY = 'X' \| 'Y'; | | | | type JK = 1 \| 2; | | | | | T1 & T2 | T1 ∩ T2 (intersezione) | type X = \{ a: string \} | | | | type Y = \{ b: string \} | | | | type XY = X & Y | | | | const x: XY = \{ a: 'a', b: 'b' \} | | | | | unknown | Insieme universale | const x: unknown = 1 | Un'unione (T1 | T2) crea un insieme più ampio (entrambi): ```typescript type X = { a: string; }; type Y = { b: string; }; type XY = X | Y; const r: XY = { a: 'a', b: 'x' }; // Valido ``` Un'intersezione (T1 e T2) crea un insieme più ristretto (solo condiviso): ```typescript type X = { a: string; }; type Y = { a: string; b: string; }; type XY = X & Y; const r: XY = { a: 'a' }; // Non valido const j: XY = { a: 'a', b: 'b' }; // Valido ``` La parola chiave `extends` potrebbe essere considerata un "sottoinsieme di" in questo contesto. Imposta un vincolo per un tipo. L'extends utilizzato con un generico, considera il generico come un insieme infinito e lo vincola a un tipo più specifico. Si noti che ``extends` non ha nulla a che fare con la gerarchia in senso OOP (questo concetto non esiste in TypeScript). TypeScript funziona con insiemi e non ha una gerarchia rigida; infatti, come nell'esempio seguente, due tipi potrebbero sovrapporsi senza che uno dei due sia un sottotipo dell'altro (TypeScript considera la struttura e la forma degli oggetti). ```typescript interface X { a: string; } interface Y extends X { b: string; } interface Z extends Y { c: string; } const z: Z = { a: 'a', b: 'b', c: 'c' }; interface X1 { a: string; } interface Y1 { a: string; b: string; } interface Z1 { a: string; b: string; c: string; } const z1: Z1 = { a: 'a', b: 'b', c: 'c' }; const r: Z1 = z; // Valido ``` ### Assegnare un tipo: Dichiarazioni di tipo e asserzioni di tipo Un tipo può essere assegnato in diversi modi in TypeScript: #### Dichiarazione di tipo Nell'esempio seguente, utilizziamo x: X (": Type") per dichiarare un tipo per la variabile x. ```typescript type X = { a: string; }; // Dichiarazione di tipo const x: X = { a: 'a', }; ``` Se la variabile non è nel formato specificato, TypeScript segnalerà un errore. Ad esempio: ```typescript type X = { a: string; }; const x: X = { a: 'a', b: 'b', // Errore: il letterale dell'oggetto può specificare solo proprietà note }; ``` #### Asserzione di tipo È possibile aggiungere un'asserzione utilizzando la parola chiave `as`. Questo indica al compilatore che lo sviluppatore ha maggiori informazioni su un tipo e silenzia eventuali errori. Ad esempio: ```typescript type X = { a: string; }; const x = { a: 'a', b: 'b', } as X; ``` Nell'esempio precedente, si asserisce che l'oggetto x abbia il tipo X utilizzando la parola chiave as. Questo informa il compilatore TypeScript che l'oggetto è conforme al tipo specificato, anche se ha una proprietà aggiuntiva b non presente nella definizione del tipo. Le asserzioni di tipo sono utili in situazioni in cui è necessario specificare un tipo più specifico, soprattutto quando si lavora con il DOM. Ad esempio: ```typescript const myInput = document.getElementById('my_input') as HTMLInputElement; ``` Qui, l'asserzione di tipo come HTMLInputElement viene utilizzata per indicare a TypeScript che il risultato di getElementById deve essere trattato come un HTMLInputElement. Le asserzioni di tipo possono anche essere utilizzate per rimappare le chiavi, come mostrato nell'esempio seguente con letterali di template: ```typescript type J = { [Property in keyof Type as `prefix_${string & Property}`]: () => Type[Property]; }; type X = { a: string; b: number; }; type Y = J; ``` In questo esempio, il tipo `J` utilizza un tipo mappato con un letterale template per rimappare le chiavi di Tipo. Crea nuove proprietà con un "prefisso\_" aggiunto a ciascuna chiave e i valori corrispondenti sono funzioni che restituiscono i valori delle proprietà originali. È importante notare che quando si utilizza un'asserzione di tipo, TypeScript non eseguirà controlli di proprietà eccessivi. Pertanto, è generalmente preferibile utilizzare una Dichiarazione di Tipo quando la struttura dell'oggetto è nota in anticipo. #### Dichiarazioni Ambientali Le dichiarazioni Ambientali sono file che descrivono i tipi per il codice JavaScript e hanno un formato di nome file come `.d.ts.`. Di solito vengono importate e utilizzate per annotare librerie JavaScript esistenti o per aggiungere tipi a file JS esistenti nel progetto. Molti tipi di librerie comuni sono disponibili all'indirizzo: [https://github.com/DefinitelyTyped/DefinitelyTyped/](https://github.com/DefinitelyTyped/DefinitelyTyped/) e possono essere installate tramite: ```shell npm install --save-dev @types/library-name ``` Per le dichiarazioni di ambiente definite, è possibile importarle utilizzando il riferimento "tripla barra": ```typescript /// ``` È possibile utilizzare le dichiarazioni di ambiente anche all'interno di file JavaScript utilizzando `// @ts-check`. La parola chiave `declare` abilita le definizioni di tipo per il codice JavaScript esistente senza importarlo, fungendo da segnaposto per i tipi da un altro file o a livello globale. ### Controllo delle proprietà e controllo delle proprietà in eccesso TypeScript si basa su un sistema di tipi strutturale, ma il controllo delle proprietà in eccesso è una proprietà di TypeScript che gli consente di verificare se un oggetto possiede esattamente le proprietà specificate nel tipo. Il controllo delle proprietà in eccesso viene eseguito, ad esempio, quando si assegnano letterali di oggetto a variabili o quando li si passa come argomenti alla proprietà in eccesso di una funzione. ```typescript type X = { a: string; }; const y = { a: 'a', b: 'b' }; const x: X = y; // Valido perché tipizzazione strutturale const w: X = { a: 'a', b: 'b' }; // Non valido perché controllo delle proprietà in eccesso ``` ### Tipi deboli Un tipo è considerato debole quando contiene solo un insieme di proprietà completamente opzionali: ```typescript type X = { a?: string; b?: string; }; ``` TypeScript considera un errore assegnare qualsiasi cosa a un tipo debole quando non c'è sovrapposizione, ad esempio, il seguente codice genera un errore: ```typescript type Options = { a?: string; b?: string; }; const fn = (options: Options) => undefined; fn({ c: 'c' }); // Non valido ``` Sebbene non sia consigliato, se necessario, è possibile bypassare questo controllo utilizzando l'asserzione di tipo: ```typescript type Options = { a?: string; b?: string; }; const fn = (options: Options) => undefined; fn({ c: 'c' } as Options); // Valido ``` Oppure aggiungendo `unknown` alla firma dell'indice del tipo debole: ```typescript type Options = { [prop: string]: unknown; a?: string; b?: string; }; const fn = (options: Options) => undefined; fn({ c: 'c' }); // Valido ``` ### Controllo rigoroso dei letterali di oggetto (freschezza) Il controllo rigoroso dei letterali di oggetto, a volte chiamato "freschezza", è una funzionalità di TypeScript che aiuta a individuare proprietà in eccesso o con errori di ortografia che altrimenti passerebbero inosservate nei normali controlli di tipo strutturale. Quando si crea un letterale di oggetto, il compilatore TypeScript lo considera "fresco". Se il letterale di oggetto viene assegnato a una variabile o passato come parametro, TypeScript genererà un errore se il letterale di oggetto specifica proprietà che non esistono nel tipo di destinazione. Tuttavia, la "freschezza" scompare quando un letterale di oggetto viene ampliato o viene utilizzata un'asserzione di tipo. Ecco alcuni esempi per illustrare: ```typescript type X = { a: string }; type Y = { a: string; b: string }; let x: X; x = { a: 'a', b: 'b' }; // Controllo di freschezza: Assegnazione non valida var y: Y; y = { a: 'a', bx: 'bx' }; // Controllo di freschezza: Assegnazione non valida const fn = (x: X) => console.log(x.a); fn(x); fn(y); // Allargamento: Nessun errore, strutturalmente compatibile con il tipo fn({ a: 'a', bx: 'b' }); // Controllo di aggiornamento: argomento non valido let c: X = { a: 'a' }; let d: Y = { a: 'a', b: '' }; c = d; // Allargamento: nessun controllo di aggiornamento ``` ### Inferenza di tipo TypeScript può inferire i tipi quando non viene fornita alcuna annotazione durante: * Inizializzazione delle variabili. * Inizializzazione dei membri. * Impostazione dei valori predefiniti per i parametri. * Tipo di ritorno della funzione. Ad esempio: ```typescript let x = 'x'; // Il tipo inferito è una stringa ``` Il compilatore TypeScript analizza il valore o l'espressione e ne determina il tipo in base alle informazioni disponibili. ### Inferenze più avanzate Quando si utilizzano più espressioni nell'inferenza di tipo, TypeScript cerca i "tipi più comuni". Ad esempio: ```typescript let x = [1, 'x', 1, null]; // Il tipo dedotto è: (string | number | null)[] ``` Se il compilatore non riesce a trovare i tipi comuni migliori, restituisce un tipo unione. Ad esempio: ```typescript let x = [new RegExp('x'), new Date()]; // Il tipo inferito è: (RegExp | Date)[] ``` TypeScript utilizza la "tipizzazione contestuale" basata sulla posizione della variabile per inferire i tipi. Nell'esempio seguente, il compilatore sa che `e` è di tipo `MouseEvent` grazie al tipo di evento `click` definito nel file lib.d.ts, che contiene dichiarazioni ambientali per vari costrutti JavaScript comuni e il DOM: ```typescript window.addEventListener('click', function (e) {}); // Il tipo inferito di e è MouseEvent ``` ### Allargamento di tipo L'allargamento di tipo è il processo in cui TypeScript assegna un tipo a una variabile inizializzata quando non è stata fornita alcuna annotazione di tipo. Consente il passaggio da tipi stretti a più ampi, ma non viceversa. Nell'esempio seguente: ```typescript let x = 'x'; // TypeScript inferisce come stringa, un tipo ampio let y: 'y' | 'x' = 'y'; // Il tipo y è un'unione di tipi letterali y = x; // Il tipo non valido 'string' non è assegnabile al tipo '"x" | "y"'. ``` TypeScript assegna `string` a `x` in base al singolo valore fornito durante l'inizializzazione (`x`); questo è un esempio di ampliamento. TypeScript fornisce modi per controllare il processo di ampliamento, ad esempio utilizzando "const". ### Const L'utilizzo della parola chiave `const` durante la dichiarazione di una variabile produce un'inferenza di tipo più ristretta in TypeScript. Ad esempio: ```typescript const x = 'x'; // TypeScript deduce il tipo di x come 'x', un tipo più ristretto let y: 'y' | 'x' = 'y'; y = x; // Valido: il tipo di x viene dedotto come 'x' ``` Utilizzando `const` per dichiarare la variabile x, il suo tipo viene ristretto allo specifico valore letterale 'x'. Poiché il tipo di x viene ristretto, può essere assegnato alla variabile y senza errori. Il motivo per cui il tipo può essere dedotto è che le variabili `const` non possono essere riassegnate, quindi il loro tipo può essere ristretto a un tipo letterale specifico, in questo caso, il tipo letterale 'x'. #### Modificatore Const sui parametri di tipo Dalla versione 5.0 di TypeScript, è possibile specificare l'attributo `const` su un parametro di tipo generico. Questo consente di dedurre il tipo più preciso possibile. Vediamo un esempio senza usare `const`: ```typescript function identity(value: T) { // Nessuna costante qui return value; } const values = identity({ a: 'a', b: 'b' }); // Il tipo inferito è: { a: string; b: string; } ``` Come puoi vedere, le proprietà `a` e `b` vengono inferite con un tipo `string`. Ora, vediamo la differenza con la versione `const`: ```typescript function identity(value: T) { // Utilizzo del modificatore const sui parametri di tipo return value; } const values = identity({ a: 'a', b: 'b' }); // Il tipo inferito è: { a: "a"; b: "b"; } ``` Ora possiamo vedere che le proprietà `a` e `b` vengono dedotte come `const`, quindi `a` e `b` vengono trattate come stringhe letterali anziché come semplici tipi `string`. #### Asserzione Const Questa funzionalità consente di dichiarare una variabile con un tipo letterale più preciso in base al suo valore di inizializzazione, indicando al compilatore che il valore deve essere trattato come un letterale immutabile. Ecco alcuni esempi: Su una singola proprietà: ```typescript const v = { x: 3 as const, }; v.x = 3; ``` Su un intero oggetto: ```typescript const v = { x: 1, y: 2, } as const; ``` Questo può essere particolarmente utile quando si definisce il tipo per una tupla: ```typescript const x = [1, 2, 3]; // number[] const y = [1, 2, 3] as const; // Tupla di readonly [1, 2, 3] ``` ### Annotazione di tipo esplicita Possiamo essere specifici e passare un tipo, nell'esempio seguente la proprietà `x` è di tipo `number`: ```typescript const v = { x: 1, // Inferred type: number (widening) }; v.x = 3; // Valido ``` Possiamo rendere l'annotazione di tipo più specifica utilizzando un'unione di tipi letterali: ```typescript const v: { x: 1 | 2 | 3 } = { x: 1, // x è ora un'unione di tipi letterali: 1 | 2 | 3 }; v.x = 3; // Valido v.x = 100; // Non valido ``` ### Restringimento dei tipi Il restringimento dei tipi è il processo in TypeScript in cui un tipo generico viene ridotto a un tipo più specifico. Ciò si verifica quando TypeScript analizza il codice e determina che determinate condizioni o operazioni possono perfezionare le informazioni sul tipo. Il restringimento dei tipi può avvenire in diversi modi, tra cui: #### Condizioni Utilizzando istruzioni condizionali, come `if` o `switch`, TypeScript può restringere il tipo in base al risultato della condizione. Ad esempio: ```typescript let x: number | undefined = 10; if (x !== undefined) { x += 100; // Il tipo è number, che è stato ristretto dalla condizione } ``` #### Generazione o restituzione Generare un errore o restituire un'istruzione in anticipo da un branch può essere utilizzato per aiutare TypeScript a restringere un tipo. Ad esempio: ```typescript let x: number | undefined = 10; if (x === undefined) { throw 'error'; } x += 100; ``` Altri modi per restringere i tipi in TypeScript includono: * Operatore `instanceof`: utilizzato per verificare se un oggetto è un'istanza di una classe specifica. * Operatore `in`: utilizzato per verificare se una proprietà esiste in un oggetto. * Operatore `typeof`: utilizzato per verificare il tipo di un valore in fase di esecuzione. * Funzioni integrate come `Array.isArray()`: utilizzate per verificare se un valore è un array. #### Unione Discriminata L'utilizzo di una "Unione Discriminata" è un pattern in TypeScript in cui un "tag" esplicito viene aggiunto agli oggetti per distinguere i diversi tipi all'interno di un'unione. Questo pattern è anche definito "unione con tag". Nell'esempio seguente, il "tag" è rappresentato dalla proprietà "type": ```typescript type A = { type: 'type_a'; value: number }; type B = { type: 'type_b'; value: string }; const x = (input: A | B): string | number => { switch (input.type) { case 'type_a': return input.value + 100; // il tipo è A case 'type_b': return input.value + 'extra'; // il tipo è B } }; ``` #### Protezioni di tipo definite dall'utente Nei casi in cui TypeScript non sia in grado di determinare un tipo, è possibile scrivere una funzione di supporto nota come "protezione di tipo definita dall'utente". Nell'esempio seguente, utilizzeremo un predicato di tipo per restringere il tipo dopo aver applicato un determinato filtro: ```typescript const data = ['a', null, 'c', 'd', null, 'f']; const r1 = data.filter(x => x != null); // Il tipo è (string | null)[], TypeScript non è riuscito a dedurre correttamente il tipo const isValid = (item: string | null): item is string => item !== null; // Protezione personalizzata del tipo const r2 = data.filter(isValid); // Il tipo ora è corretto string[], utilizzando la protezione del tipo predicato siamo riusciti a restringere il tipo ``` ## Tipi primitivi TypeScript supporta 7 tipi primitivi. Un tipo di dati primitivo si riferisce a un tipo che non è un oggetto e non ha metodi associati. In TypeScript, tutti i tipi primitivi sono immutabili, il che significa che i loro valori non possono essere modificati una volta assegnati. ### string Il tipo primitivo `string` memorizza dati testuali e il valore è sempre racchiuso tra virgolette doppie o singole. ```typescript const x: string = 'x'; const y: string = 'y'; ``` Le stringhe possono estendersi su più righe se racchiuse dal carattere di apice inverso (`): ```typescript let sentence: string = `xxx, yyy`; ``` ### boolean Il tipo di dati `boolean` in TypeScript memorizza un valore binario, `true` o `false`. ```typescript const isReady: boolean = true; ``` ### number Un tipo di dati `number` in TypeScript è rappresentato da un valore in virgola mobile a 64 bit. Un tipo di dati `number` può rappresentare numeri interi e frazioni. TypeScript supporta anche i sistemi di numerazione esadecimale, binario e ottale, ad esempio: ```typescript const decimal: number = 10; const hexadecimal: number = 0xa00d; // L'esadecimale inizia con 0x const binary: number = 0b1010; // Il binario inizia con 0b const octal: number = 0o633; // L'ottale inizia con 0o ``` ### bigInt Un `bigInt` rappresenta valori numerici molto grandi (253 – 1) e non possono essere rappresentati con un `number`. Un `bigInt` può essere creato chiamando la funzione integrata `BigInt()` o aggiungendo `n` alla fine di qualsiasi letterale numerico intero: ```typescript const x: bigint = BigInt(9007199254740991); const y: bigint = 9007199254740991n; ``` Note: * I valori `bigInt` non possono essere combinati con `number` e non possono essere utilizzati con `Math` integrato, devono essere forzati allo stesso tipo. * I valori `bigInt` sono disponibili solo se la configurazione di destinazione è ES2020 o superiore. ### Simbolo I simboli sono identificatori univoci che possono essere utilizzati come chiavi di proprietà negli oggetti per evitare conflitti di denominazione. ```typescript type Obj = { [sym: symbol]: number; }; const a = Symbol('a'); const b = Symbol('b'); let obj: Obj = {}; obj[a] = 123; obj[b] = 456; console.log(obj[a]); // 123 console.log(obj[b]); // 456 ``` ### null e undefined I tipi `null` e `undefined` rappresentano entrambi nessun valore o l'assenza di qualsiasi valore. Il tipo `undefined` indica che il valore non è assegnato o inizializzato o indica un'assenza involontaria di valore. Il tipo `null` indica che sappiamo che il campo non ha un valore, quindi il valore non è disponibile, e indica un'assenza intenzionale di valore. ### Array Un `array` è un tipo di dati che può memorizzare più valori dello stesso tipo o meno. Può essere definito utilizzando la seguente sintassi: ```typescript const x: string[] = ['a', 'b']; const y: Array = ['a', 'b']; const j: Array = ['a', 1, 'b', 2]; // Unione ``` TypeScript supporta array di sola lettura utilizzando la seguente sintassi: ```typescript const x: readonly string[] = ['a', 'b']; // Modificatore di sola lettura const y: ReadonlyArray = ['a', 'b']; const j: ReadonlyArray = ['a', 1, 'b', 2]; j.push('x'); // Non valido ``` TypeScript supporta tuple e tuple di sola lettura: ```typescript const x: [string, number] = ['a', 1]; const y: readonly [string, number] = ['a', 1]; ``` ### any Il tipo di dati ``any` rappresenta letteralmente un valore "qualsiasi", ed è il valore predefinito quando TypeScript non può dedurre il tipo o non è specificato. Quando si utilizza `any`, il compilatore TypeScript salta il controllo del tipo, quindi non c'è sicurezza di tipo quando si utilizza `any`. In genere, non utilizzare `any` per silenziare il compilatore quando si verifica un errore, ma concentrarsi sulla correzione dell'errore, poiché utilizzando `any` è possibile interrompere i contratti e perdere i vantaggi del completamento automatico di TypeScript. Il tipo `any` potrebbe essere utile durante una migrazione graduale da JavaScript a TypeScript, in quanto può silenziare il compilatore. Per i nuovi progetti, utilizzare la configurazione TypeScript `noImplicitAny`, che consente a TypeScript di generare errori quando viene utilizzato o dedotto `any`. Il tipo `any` è solitamente fonte di errori che possono mascherare problemi reali con i tipi. Evitatelo il più possibile. ## Annotazioni di tipo Sulle variabili dichiarate usando `var`, `let` e `const`, è possibile aggiungere facoltativamente un tipo: ```typescript const x: number = 1; ``` TypeScript esegue un buon lavoro nell'inferenza dei tipi, soprattutto quando si tratta di tipi semplici, quindi queste dichiarazioni nella maggior parte dei casi non sono necessarie. Sulle funzioni è possibile aggiungere annotazioni di tipo ai parametri: ```typescript function sum(a: number, b: number) { return a + b; } ``` Il seguente è un esempio che utilizza una funzione anonima (la cosiddetta funzione lambda): ```typescript const sum = (a: number, b: number) => a + b; ``` Queste annotazioni possono essere evitate quando è presente un valore predefinito per un parametro: ```typescript const sum = (a = 10, b: number) => a + b; ``` Le annotazioni del tipo di ritorno possono essere aggiunte alle funzioni: ```typescript const sum = (a = 10, b: number): number => a + b; ``` Questo è utile soprattutto per le funzioni più complesse, poiché scrivere esplicitamente il tipo di ritorno prima di un'implementazione può aiutare a pensare meglio alla funzione. In genere, si consiglia di annotare le firme dei tipi, ma non le variabili locali del corpo, e di aggiungere i tipi sempre ai letterali degli oggetti. ## Proprietà facoltative Un oggetto può specificare Proprietà facoltative aggiungendo un punto interrogativo `?` alla fine del nome della proprietà: ```typescript type X = { a: number; b?: number; // Facoltativo }; ``` È possibile specificare un valore predefinito quando una proprietà è facoltativa" ```typescript type X = { a: number; b?: number; }; const x = ({ a, b = 100 }: X) => a + b; ``` ## Proprietà di sola lettura È possibile impedire la scrittura su una proprietà utilizzando il modificatore `readonly`, che assicura che la proprietà non possa essere riscritta ma non fornisce alcuna garanzia di immutabilità totale: ```typescript interface Y { readonly a: number; } type X = { readonly a: number; }; type J = Readonly<{ a: number; }>; type K = { readonly [index: number]: string; }; ``` ## Firme di indice In TypeScript possiamo usare come firma di indice `string`, `number` e `symbol`: ```typescript type K = { [name: string | number]: string; }; const k: K = { x: 'x', 1: 'b' }; console.log(k['x']); console.log(k[1]); console.log(k['1']); // Stesso risultato di k[1] ``` Si noti che JavaScript converte automaticamente un indice con `number` in un indice con `string`, quindi `k[1]` o `k["1"]` restituiscono lo stesso valore. ## Estensione dei tipi È possibile estendere un'`interfaccia` (copiare membri da un altro tipo): ```typescript interface X { a: string; } interface Y extends X { b: string; } ``` È anche possibile estendere da più tipi: ```typescript interface A { a: string; } interface B { b: string; } interface Y extends A, B { y: string; } ``` La parola chiave `extends` funziona solo su interfacce e classi; per i tipi utilizzare un'intersezione: ```typescript type A = { a: number; }; type B = { b: number; }; type C = A & B; ``` È possibile estendere un tipo utilizzando un'inferenza, ma non viceversa: ```typescript type A = { a: string; }; interface B extends A { b: string; } ``` ## Tipi letterali Un tipo letterale è un singolo insieme di elementi di un tipo collettivo; definisce un valore molto preciso che è una primitiva JavaScript. I tipi letterali in TypeScript sono numeri, stringhe e booleani. Esempio di letterali: ```typescript const a = 'a'; // Stringa tipo letterale const b = 1; // Numeric literal type const c = true; // Boolean literal type ``` I tipi letterali stringa, numerico e booleano vengono utilizzati nell'unione, nella protezione dei tipi e negli alias di tipo. Nell'esempio seguente, è possibile vedere un alias di tipo unione. `O` è costituito solo dai valori specificati, nessun'altra stringa è valida: ```typescript type O = 'a' | 'b' | 'c'; ``` ## Inferenza letterale L'inferenza letterale è una funzionalità di TypeScript che consente di dedurre il tipo di una variabile o di un parametro in base al suo valore. Nell'esempio seguente possiamo vedere che TypeScript considera `x` un tipo letterale in quanto il valore non può essere modificato in seguito, mentre `y` viene dedotto come stringa in quanto può essere modificato in seguito. ```typescript const x = 'x'; // Literal type of 'x', because this value cannot be changed let y = 'y'; // Type string, because we can change this value ``` Nell'esempio seguente possiamo vedere che `o.x` è stato dedotto come `string` (e non come un letterale di `a`), poiché TypeScript considera che il valore possa essere modificato in qualsiasi momento successivo. ```typescript type X = 'a' | 'b'; let o = { x: 'a', // Questa è una stringa più ampia }; const fn = (x: X) => `${x}-foo`; console.log(fn(o.x)); // L'argomento di tipo 'string' non è assegnabile al parametro di tipo 'X' ``` Come puoi vedere, il codice genera un errore quando si passa `o.x` a `fn`, poiché X è un tipo più ristretto. Possiamo risolvere questo problema utilizzando l'asserzione di tipo `const` o il tipo `X`: ```typescript let o = { x: 'a' as const, }; ``` oppure: ```typescript let o = { x: 'a' as X, }; ``` ## strictNullChecks `strictNullChecks` è un'opzione del compilatore TypeScript che impone un controllo null rigoroso. Quando questa opzione è abilitata, variabili e parametri possono essere assegnati a `null` o `undefined` solo se sono stati dichiarati esplicitamente di quel tipo utilizzando l'unione `null` | `undefined`. Se una variabile o un parametro non viene dichiarato esplicitamente come nullable, TypeScript genererà un errore per prevenire potenziali errori di runtime. ## Enumerazioni In TypeScript, un `enum` è un insieme di valori costanti denominati. ```typescript enum Colore { Rosso = '#ff0000', Verde = '#00ff00', Blu = '#0000ff', } ``` Gli enum possono essere definiti in diversi modi: ### Enumerazioni numeriche In TypeScript, un enum numerico è un enum in cui a ogni costante viene assegnato un valore numerico, a partire da 0 per impostazione predefinita. ```typescript enum Size { Small, // il valore inizia da 0 Medium, Large, } ``` È possibile specificare valori personalizzati assegnandoli esplicitamente: ```typescript enum Size { Small = 10, Medium, Large, } console.log(Size.Medium); // 11 ``` ### Enum String In TypeScript, un enum String è un enum in cui a ogni costante viene assegnato un valore stringa. ```typescript enum Language { English = 'EN', Spanish = 'ES', } ``` Nota: TypeScript consente l'utilizzo di enum eterogenei in cui stringhe e membri numerici possono coesistere. ### Enum Constant Un enum Constant in TypeScript è un tipo speciale di enum in cui tutti i valori sono noti in fase di compilazione e vengono inlineati ovunque venga utilizzato l'enum, con conseguente maggiore efficienza del codice. ```typescript const enum Language { English = 'EN', Spanish = 'ES', } console.log(Language.English); ``` Verrà compilato in: ```typescript console.log('EN' /* Language.English */); ``` Note: Gli enum costanti hanno valori hardcoded, che cancellano l'enum, il che può essere più efficiente nelle librerie autonome, ma generalmente non è auspicabile. Inoltre, gli enum costanti non possono avere membri calcolati. ### Mapping inverso In TypeScript, i mapping inversi negli enum si riferiscono alla possibilità di recuperare il nome del membro dell'enum dal suo valore. Per impostazione predefinita, i membri dell'enum hanno mapping in avanti dal nome al valore, ma i mapping inversi possono essere creati impostando esplicitamente i valori per ciascun membro. I mapping inversi sono utili quando è necessario cercare un membro dell'enum in base al suo valore o quando è necessario iterare su tutti i membri dell'enum. Si noti che solo i membri dell'enum numerico genereranno mapping inversi, mentre i membri dell'enum stringa non generano alcun mapping inverso. Il seguente enum: ```typescript enum Grade { A = 90, B = 80, C = 70, F = 'fail', } ``` Compila in: ```javascript "use strict"; var Grade; (function (Grade) { Grade[(Grade["A"] = 90)] = "A"; Grade[(Grade["B"] = 80)] = "B"; Grade[(Grade["C"] = 70)] = "C"; Grade["F"] = "fail"; })(Grade || (Grade = {})); ``` Pertanto, la mappatura dei valori alle chiavi funziona per i membri enum numerici, ma non per i membri enum stringa: ```typescript enum Grade { A = 90, B = 80, C = 70, F = 'fail', } const myGrade = Grade.A; console.log(Grade[myGrade]); // A console.log(Grade[90]); // A const failGrade = Grade.F; console.log(failGrade); // fail console.log(Grade[failGrade]); // Element ha implicitamente un tipo 'any' perché l'espressione indice non è di tipo 'number'. ``` ### Enum ambientali Un enum ambientale in TypeScript è un tipo di Enum definito in un file di dichiarazione (\*.d.ts) senza un'implementazione associata. Permette di definire un set di costanti denominate che possono essere utilizzate in modo sicuro tra file diversi senza dover importare i dettagli di implementazione in ogni file. ### Membri calcolati e costanti In TypeScript, un membro calcolato è un membro di un Enum il cui valore è calcolato in fase di esecuzione, mentre un membro costante è un membro il cui valore è impostato in fase di compilazione e non può essere modificato in fase di esecuzione. I membri calcolati sono consentiti negli Enum normali, mentre i membri costanti sono consentiti sia negli enum normali che in quelli costanti. ```typescript // Membri costanti enum Color { Red = 1, Green = 5, Blue = Red + Green, } console.log(Color.Blue); // 6 generazioni in fase di compilazione ``` ```typescript // Membri calcolati enum Color { Red = 1, Green = Math.pow(2, 2), Blue = Math.floor(Math.random() * 3) + 1, } console.log(Color.Blue); // numero casuale generato in fase di esecuzione ``` Gli enum sono indicati da unioni che comprendono i loro tipi di membri. I valori di ciascun membro possono essere determinati tramite espressioni costanti o non costanti, con i membri che possiedono valori costanti a cui vengono assegnati tipi letterali. Per illustrare, si consideri la dichiarazione del tipo E e dei suoi sottotipi E.A, E.B ed E.C. In questo caso, E rappresenta l'unione E.A | E.B | E.C. ```typescript const identity = (value: number) => value; enum E { A = 2 * 5, // Letterale numerico B = 'bar', // Letterale stringa C = identity(42), // Calcolato opaco } console.log(E.C); //42 ``` ## Restringimento Il restringimento di TypeScript è il processo di perfezionamento del tipo di una variabile all'interno di un blocco condizionale. Questo è utile quando si lavora con tipi union, in cui una variabile può avere più di un tipo. TypeScript riconosce diversi modi per restringere il tipo: ### protezioni di tipo typeof Il type guard typeof è uno specifico type guard in TypeScript che controlla il tipo di una variabile in base al suo tipo JavaScript predefinito. ```typescript const fn = (x: number | string) => { if (typeof x === 'number') { return x + 1; // x è un numero } return -1; }; ``` ### Restringimento di veridicità Il restringimento di veridicità in TypeScript funziona verificando se una variabile è vera o falsa, per restringerne di conseguenza il tipo. ```typescript const toUpperCase = (name: string | null) => { if (name) { return name.toUpperCase(); } else { return null; } }; ``` ### Restringimento di uguaglianza Il restringimento di uguaglianza in TypeScript funziona verificando se una variabile è uguale o meno a un valore specifico, per restringerne di conseguenza il tipo. Viene utilizzato insieme alle istruzioni `switch` e agli operatori di uguaglianza come `===`, `!==`, `==` e `!=` per restringere i tipi. ```typescript const checkStatus = (status: 'success' | 'error') => { switch (status) { case 'success': return true; case 'error': return null; } }; ``` ### Restringimento dell'operatore `in` Il restringimento dell'operatore `in` in TypeScript è un modo per restringere il tipo di una variabile in base all'esistenza di una proprietà all'interno del tipo della variabile. ```typescript type Dog = { name: string; breed: string; }; type Cat = { name: string; likesCream: boolean; }; const getAnimalType = (pet: Dog | Cat) => { if ('breed' in pet) { return 'dog'; } else { return 'cat'; } }; ``` ### Restringimento instanceof L'operatore di restringimento `instanceof` in TypeScript è un modo per restringere il tipo di una variabile in base alla sua funzione costruttore, verificando se un oggetto è un'istanza di una determinata classe o interfaccia. ```typescript class Square { constructor(public width: number) {} } class Rectangle { constructor( public width: number, public height: number ) {} } function area(shape: Square | Rectangle) { if (shape instanceof Square) { return shape.width * shape.width; } else { return shape.width * shape.height; } } const square = new Square(5); const rectangle = new Rectangle(5, 10); console.log(area(square)); // 25 console.log(area(rectangle)); // 50 ``` ## Assegnazioni Il restringimento TypeScript tramite assegnazioni è un modo per restringere il tipo di una variabile in base al valore assegnato. Quando a una variabile viene assegnato un valore, TypeScript ne deduce il tipo in base al valore assegnato e restringe il tipo della variabile in modo che corrisponda al tipo dedotto. ```typescript let value: string | number; value = 'hello'; if (typeof value === 'string') { console.log(value.toUpperCase()); } value = 42; if (typeof value === 'number') { console.log(value.toFixed(2)); } ``` ## Analisi del flusso di controllo L'analisi del flusso di controllo in TypeScript è un modo per analizzare staticamente il flusso di codice per dedurre i tipi di variabili, consentendo al compilatore di restringere i tipi di tali variabili secondo necessità, in base ai risultati dell'analisi. Prima di TypeScript 4.4, l'analisi del flusso di codice veniva applicata solo al codice all'interno di un'istruzione if, ma da TypeScript 4.4 può essere applicata anche alle espressioni condizionali e agli accessi alle proprietà discriminanti referenziati indirettamente tramite variabili const. Ad esempio: ```typescript const f1 = (x: unknown) => { const isString = typeof x === 'string'; if (isString) { x.length; } }; const f2 = ( obj: { kind: 'foo'; foo: string } | { kind: 'bar'; bar: number } ) => { const isFoo = obj.kind === 'foo'; if (isFoo) { obj.foo; } else { obj.bar; } }; ``` Alcuni esempi in cui il restringimento non avviene: ```typescript const f1 = (x: unknown) => { let isString = typeof x === 'string'; if (isString) { x.length; // Errore, nessun restringimento perché isString non è costante } }; const f6 = ( obj: { kind: 'foo'; foo: string } | { kind: 'bar'; bar: number } ) => { const isFoo = obj.kind === 'foo'; obj = obj; if (isFoo) { obj.foo; // Errore, nessun restringimento perché obj è assegnato nel corpo della funzione } }; ``` Note: Nelle espressioni condizionali vengono analizzati fino a cinque livelli di indirezione. ## Predicati di tipo I predicati di tipo in TypeScript sono funzioni che restituiscono un valore booleano e vengono utilizzate per restringere il tipo di una variabile a un tipo più specifico. ```typescript const isString = (value: unknown): value is string => typeof value === 'string'; const foo = (bar: unknown) => { if (isString(bar)) { console.log(bar.toUpperCase()); } else { console.log('not a string'); } }; ``` ## Unioni Discriminate Le unioni Discriminate in TypeScript sono un tipo di unione che utilizza una proprietà comune, nota come discriminante, per restringere l'insieme dei tipi possibili per l'unione. ```typescript type Square = { kind: 'square'; // Discriminante size: number; }; type Circle = { kind: 'circle'; // Discriminante radius: number; }; type Shape = Square | Circle; const area = (shape: Shape) => { switch (shape.kind) { case 'square': return Math.pow(shape.size, 2); case 'circle': return Math.PI * Math.pow(shape.radius, 2); } }; const square: Square = { kind: 'square', size: 5 }; const circle: Circle = { kind: 'circle', radius: 2 }; console.log(area(square)); // 25 console.log(area(circle)); // 12.566370614359172 ``` ## Il tipo never Quando una variabile viene ristretta a un tipo che non può contenere alcun valore, il compilatore TypeScript dedurrà che la variabile deve essere del tipo `never`. Questo perché il tipo `never` rappresenta un valore che non può mai essere prodotto. ```typescript const printValue = (val: string | number) => { if (typeof val === 'string') { console.log(val.toUpperCase()); } else if (typeof val === 'number') { console.log(val.toFixed(2)); } else { // val ha il tipo never qui perché non può essere altro che una stringa o un numero const neverVal: never = val; console.log(`Valore imprevisto: ${neverVal}`); } }; ``` ## Controllo di esaustività Il controllo di esaustività è una funzionalità di TypeScript che garantisce che tutti i possibili casi di unione discriminata vengano gestiti in un'istruzione `switch` o in un'istruzione `if`. ```typescript type Direction = 'up' | 'down'; const move = (direction: Direction) => { switch (direction) { case 'up': console.log("Spostamento verso l'alto"); break; case 'down': console.log('Spostamento verso il basso'); break; default: const exhaustiveCheck: never = direction; console.log(exhaustiveCheck); // Questa riga non verrà mai eseguita } }; ``` Il tipo `never` viene utilizzato per garantire che il caso predefinito sia esaustivo e che TypeScript generi un errore se un nuovo valore viene aggiunto al tipo Direction senza essere gestito nell'istruzione switch. ## Tipi di oggetto In TypeScript, i tipi di oggetto descrivono la forma di un oggetto. Specificano i nomi e i tipi delle proprietà dell'oggetto, nonché se tali proprietà sono obbligatorie o facoltative. In TypeScript, è possibile definire i tipi di oggetto in due modi principali: interface, che definisce la forma di un oggetto specificando i nomi, i tipi e l'opzionalità delle sue proprietà. ```typescript interface User { name: string; age: number; email?: string; } ``` Un alias di tipo, simile a un'interfaccia, definisce la forma di un oggetto. Tuttavia, può anche creare un nuovo tipo personalizzato basato su un tipo esistente o su una combinazione di tipi esistenti. Ciò include la definizione di tipi unione, tipi intersezione e altri tipi complessi. ```typescript type Point = { x: number; y: number; }; ``` È anche possibile definire un tipo in modo anonimo: ```typescript const sum = (x: { a: number; b: number }) => x.a + x.b; console.log(sum({ a: 5, b: 1 })); ``` ## Tipo di tupla (anonimo) Un tipo di tupla è un tipo che rappresenta un array con un numero fisso di elementi e i relativi tipi. Un tipo di tupla impone un numero specifico di elementi e i rispettivi tipi in un ordine fisso. I tipi di tupla sono utili quando si desidera rappresentare una raccolta di valori con tipi specifici, dove la posizione di ciascun elemento nell'array ha un significato specifico. ```typescript type Point = [number, number]; ``` ## Tipo di tupla denominato (etichettato) I tipi di tupla possono includere etichette o nomi opzionali per ciascun elemento. Queste etichette servono per migliorare la leggibilità e facilitare l'utilizzo degli strumenti e non influiscono sulle operazioni che è possibile eseguire con esse. ```typescript type T = string; type Tuple1 = [T, T]; type Tuple2 = [a: T, b: T]; type Tuple3 = [a: T, T]; // Tupla con nome più Tupla anonima ``` ## Tupla a lunghezza fissa Una tupla a lunghezza fissa è un tipo specifico di tupla che impone un numero fisso di elementi di tipi specifici e non consente alcuna modifica alla lunghezza della tupla una volta definita. Le tuple a lunghezza fissa sono utili quando è necessario rappresentare una raccolta di valori con un numero specifico di elementi e tipi specifici e si desidera garantire che la lunghezza e i tipi della tupla non possano essere modificati inavvertitamente. ```typescript const x = [10, 'hello'] as const; x.push(2); // Errore ``` ## Tipo Unione Un Tipo Unione è un tipo che rappresenta un valore che può essere di diversi tipi. I Tipi Unione sono indicati con il simbolo `|` tra ogni tipo possibile. ```typescript let x: string | number; x = 'hello'; // Valido x = 123; // Valido ``` ## Tipi Intersezione Un Tipo Intersezione è un tipo che rappresenta un valore che ha tutte le proprietà di due o più tipi. I Tipi Intersezione sono indicati con il simbolo `&` tra ogni tipo. ```typescript type X = { a: string; }; type Y = { b: string; }; type J = X & Y; // Intersezione const j: J = { a: 'a', b: 'b', }; ``` ## Indicizzazione dei tipi L'indicizzazione dei tipi si riferisce alla capacità di definire tipi che possono essere indicizzati da una chiave non nota in anticipo, utilizzando una firma di indice per specificare il tipo per le proprietà che non sono dichiarate esplicitamente. ```typescript type Dictionary = { [key: string]: T; }; const myDict: Dictionary = { a: 'a', b: 'b' }; console.log(myDict['a']); // Restituisce un ``` ## Tipo da Valore In TypeScript, il tipo da valore si riferisce all'inferenza automatica di un tipo da un valore o da un'espressione tramite inferenza di tipo. ```typescript const x = 'x'; // TypeScript inferisce 'x' come una stringa letterale con 'const' (immutabile), ma lo amplia a 'string' con 'let' (riassegnabile). ``` ## Tipo da Ritorno Funzione Il tipo da Ritorno Funzione si riferisce alla possibilità di inferire automaticamente il tipo di ritorno di una funzione in base alla sua implementazione. Ciò consente a TypeScript di determinare il tipo del valore restituito dalla funzione senza annotazioni di tipo esplicite. ```typescript const add = (x: number, y: number) => x + y; // TypeScript può dedurre che il tipo restituito dalla funzione sia un numero ``` ## Tipo da modulo Il tipo da modulo si riferisce alla possibilità di utilizzare i valori esportati di un modulo per dedurne automaticamente il tipo. Quando un modulo esporta un valore con un tipo specifico, TypeScript può utilizzare tali informazioni per dedurre automaticamente il tipo di quel valore quando viene importato in un altro modulo. ```typescript // calc.ts export const add = (x: number, y: number) => x + y; // index.ts import { add } from 'calc'; const r = add(1, 2); // r è un numero ``` ## Tipi mappati I tipi mappati in TypeScript consentono di creare nuovi tipi basati su un tipo esistente trasformando ciascuna proprietà tramite una funzione di mappatura. Mappando i tipi esistenti, è possibile creare nuovi tipi che rappresentano le stesse informazioni in un formato diverso. Per creare un tipo mappato, si accede alle proprietà di un tipo esistente utilizzando l'operatore `keyof` e quindi le si modifica per produrre un nuovo tipo. Nell'esempio seguente: ```typescript type MyMappedType = { [P in keyof T]: T[P][]; }; type MyType = { foo: string; bar: number; }; type MyNewType = MyMappedType; const x: MyNewType = { foo: ['hello', 'world'], bar: [1, 2, 3], }; ``` Definiamo MyMappedType per mappare le proprietà di T, creando un nuovo tipo con ogni proprietà come array del suo tipo originale. In questo modo, creiamo MyNewType per rappresentare le stesse informazioni di MyType, ma con ogni proprietà come array. ## Modificatori di tipo mappati I modificatori di tipo mappati in TypeScript consentono la trasformazione delle proprietà all'interno di un tipo esistente: * `readonly` o `+readonly`: questo rende una proprietà nel tipo mappato di sola lettura. * `-readonly`: questo consente a una proprietà nel tipo mappato di essere modificabile. * `?`: questo designa una proprietà nel tipo mappato come facoltativa. Esempi: ```typescript type ReadOnly = { readonly [P in keyof T]: T[P] }; // Tutte le proprietà contrassegnate come di sola lettura type Mutable = { -readonly [P in keyof T]: T[P] }; // Tutte le proprietà contrassegnate come modificabili type MyPartial = { [P in keyof T]?: T[P] }; // Tutte le proprietà contrassegnate come facoltative ``` ## Tipi condizionali I tipi condizionali sono un modo per creare un tipo che dipende da una condizione, in cui il tipo da creare viene determinato in base al risultato della condizione. Sono definiti utilizzando la parola chiave `extends` e un operatore ternario per scegliere condizionatamente tra due tipi. ```typescript type IsArray = T extends any[] ? true : false; const myArray = [1, 2, 3]; const myNumber = 42; type IsMyArrayAnArray = IsArray; // Type true type IsMyNumberAnArray = IsArray; // Type false ``` ## Tipi condizionali distributivi I tipi condizionali distributivi sono una funzionalità che consente di distribuire un tipo su un'unione di tipi, applicando una trasformazione a ciascun membro dell'unione individualmente. Questo può essere particolarmente utile quando si lavora con tipi mappati o tipi di ordine superiore. ```typescript type Nullable = T extends any ? T | null : never; type NumberOrBool = number | boolean; type NullableNumberOrBool = Nullable; // number | boolean | null ``` ## infer Inferenza di tipo nei tipi condizionali La parola chiave `infer` viene utilizzata nei tipi condizionali per inferire (estrarre) il tipo di un parametro generico da un tipo che dipende da esso. Questo consente di scrivere definizioni di tipo più flessibili e riutilizzabili. ```typescript type ElementType = T extends (infer U)[] ? U : never; type Numbers = ElementType; // number type Strings = ElementType; // string ``` ## Tipi Condizionali Predefiniti In TypeScript, i Tipi Condizionali Predefiniti sono tipi condizionali integrati forniti dal linguaggio. Sono progettati per eseguire trasformazioni di tipo comuni in base alle caratteristiche di un dato tipo. `Exclude`: questo tipo rimuove da Type tutti i tipi assegnabili a ExcludedType. `Extract`: questo tipo estrae da Union tutti i tipi assegnabili a Type. `NonNullable`: questo tipo rimuove null e undefined da Type. `ReturnType`: questo tipo estrae il tipo di ritorno di un Type di funzione. `Parameters`: questo tipo estrae i tipi di parametro di un Type di funzione. `Required`: Questo tipo rende obbligatorie tutte le proprietà in Type. `Partial`: Questo tipo rende facoltative tutte le proprietà in Type. `Readonly`: Questo tipo rende di sola lettura tutte le proprietà in Type. ## Tipi di unione di template I tipi di unione di template possono essere utilizzati per unire e manipolare il testo all'interno del sistema di tipi, ad esempio: ```typescript type Status = 'active' | 'inactive'; type Products = 'p1' | 'p2'; type ProductId = `id-${Products}-${Status}`; // "id-p1-active" | "id-p1-inactive" | "id-p2-active" | "id-p2-inactive" ``` ## Tipo Any Il tipo `any` è un tipo speciale (supertipo universale) che può essere utilizzato per rappresentare qualsiasi tipo di valore (primitive, oggetti, array, funzioni, errori, simboli). Viene spesso utilizzato in situazioni in cui il tipo di un valore non è noto in fase di compilazione, o quando si lavora con valori provenienti da API o librerie esterne che non dispongono di tipi TypeScript. Utilizzando il tipo `any`, si indica al compilatore TypeScript che i valori devono essere rappresentati senza alcuna limitazione. Per massimizzare la sicurezza dei tipi nel codice, tieni presente quanto segue: * Limitare l'utilizzo di `any` a casi specifici in cui il tipo è realmente sconosciuto. * Non restituire `any` da una funzione, poiché ciò indebolisce la sicurezza del tipo nel codice che lo utilizza. * Invece di `any`, utilizzare `@ts-ignore` se è necessario silenziare il compilatore. ```typescript let value: any; value = true; // Valido value = 7; // Valido ``` ## Tipo Unknown In TypeScript, il tipo `unknown` rappresenta un valore di tipo sconosciuto. A differenza del tipo `any`, che consente qualsiasi tipo di valore, `unknown` richiede un controllo o un'asserzione di tipo prima di poter essere utilizzato in un modo specifico, quindi non sono consentite operazioni su un `unknown` senza prima aver effettuato un'asserzione o aver limitato il campo a un tipo più specifico. Il tipo `unknown` è assegnabile solo a qualsiasi tipo e il tipo `unknown` stesso è un'alternativa type-safe ad `any`. ```typescript let value: unknown; let value1: unknown = value; // Valido let value2: any = value; // Valido let value3: boolean = value; // Non valido let value4: number = value; // Non valido ``` ```typescript const add = (a: unknown, b: unknown): number | undefined => typeof a === 'number' && typeof b === 'number' ? a + b : undefined; console.log(add(1, 2)); // 3 console.log(add('x', 2)); // non definito ``` ## Tipo Void Il tipo `void` viene utilizzato per indicare che una funzione non restituisce un valore. ```typescript const sayHello = (): void => { console.log('Hello!'); }; ``` ## Tipo Never Il tipo `never` rappresenta valori che non si verificano mai. Viene utilizzato per indicare funzioni o espressioni che non restituiscono mai né generano errori. Ad esempio, un ciclo infinito: ```typescript const infiniteLoop = (): never => { while (true) { // fai qualcosa } }; ``` Generazione di un errore: ```typescript const throwError = (message: string): never => { throw new Error(message); }; ``` Il tipo `never` è utile per garantire la sicurezza dei tipi e rilevare potenziali errori nel codice. Aiuta TypeScript ad analizzare e dedurre tipi più precisi se utilizzato in combinazione con altri tipi e istruzioni di controllo del flusso, ad esempio: ```typescript type Direction = 'up' | 'down'; const move = (direction: Direction): void => { switch (direction) { case 'up': // sposta verso l'alto break; case 'down': // sposta verso il basso break; default: const exhaustiveCheck: never = direction; throw new Error(`Unhandled direction: ${exhaustiveCheck}`); } }; ``` ## Interfaccia e tipo ### Sintassi comune In TypeScript, le interfacce definiscono la struttura degli oggetti, specificando i nomi e i tipi di proprietà o metodi che un oggetto deve avere. La sintassi comune per definire un'interfaccia in TypeScript è la seguente: ```typescript interface InterfaceName { property1: Type1; // ... method1(arg1: ArgType1, arg2: ArgType2): ReturnType; // ... } ``` Analogamente per la definizione del tipo: ```typescript type TypeName = { property1: Type1; // ... method1(arg1: ArgType1, arg2: ArgType2): ReturnType; // ... }; ``` `interface InterfaceName` o `type TypeName`: Definisce il nome dell'interfaccia. `property1`: `Type1`: Specifica le proprietà dell'interfaccia insieme ai tipi corrispondenti. È possibile definire più proprietà, ciascuna separata da un punto e virgola. `method1(arg1: ArgType1, arg2: ArgType2): ReturnType;`: Specifica i metodi dell'interfaccia. I metodi sono definiti con i loro nomi, seguiti da un elenco di parametri tra parentesi e dal tipo di ritorno. È possibile definire più metodi, ciascuno separato da un punto e virgola. Esempio di interfaccia: ```typescript interface Person { name: string; age: number; greet(): void; } ``` Esempio di tipo: ```typescript type TypeName = { property1: string; method1(arg1: string, arg2: string): string; }; ``` In TypeScript, i tipi vengono utilizzati per definire la forma dei dati e applicare il controllo dei tipi. Esistono diverse sintassi comuni per la definizione dei tipi in TypeScript, a seconda del caso d'uso specifico. Ecco alcuni esempi: ### Tipi di base ```typescript let myNumber: number = 123; // number type let myBoolean: boolean = true; // boolean type let myArray: string[] = ['a', 'b']; // array di stringhe let myTuple: [string, number] = ['a', 123]; // tupla ``` ### Oggetti e interfacce ```typescript const x: { name: string; age: number } = { name: 'Simon', age: 7 }; ``` ### Tipi di unione e intersezione ```typescript type MyType = string | number; // Union type let myUnion: MyType = 'hello'; // Can be a string myUnion = 123; // Or a number type TypeA = { name: string }; type TypeB = { age: number }; type CombinedType = TypeA & TypeB; // Intersection type let myCombined: CombinedType = { name: 'John', age: 25 }; // Object with name and age properties ``` ## Primitive di tipo predefinite TypeScript dispone di diverse primitive di tipo predefinite che possono essere utilizzate per definire variabili, parametri di funzione e tipi restituiti: * `number`: rappresenta valori numerici, inclusi numeri interi e numeri in virgola mobile. * `string`: rappresenta dati testuali. * `boolean`: rappresenta valori logici, che possono essere true o false. * `null`: rappresenta l'assenza di un valore. * `undefined`: rappresenta un valore che non è stato assegnato o non è stato definito. * `symbol`: rappresenta un identificatore univoco. I simboli vengono in genere utilizzati come chiavi per le proprietà degli oggetti. * `bigint`: rappresenta numeri interi con precisione arbitraria. * `any`: rappresenta un tipo dinamico o sconosciuto. Le variabili di tipo any possono contenere valori di qualsiasi tipo e ignorano il controllo del tipo. \* `void`: rappresenta l'assenza di qualsiasi tipo. È comunemente usato come tipo di ritorno di funzioni che non restituiscono alcun valore. * `never`: rappresenta un tipo per valori che non si verificano mai. È tipicamente usato come tipo di ritorno di funzioni che generano un errore o entrano in un ciclo infinito. ## Oggetti JavaScript predefiniti comuni TypeScript è un superset di JavaScript e include tutti gli oggetti JavaScript predefiniti comunemente usati. Un elenco completo di questi oggetti è disponibile sul sito web di documentazione di Mozilla Developer Network (MDN): [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects) Ecco un elenco di alcuni oggetti JavaScript predefiniti comunemente utilizzati: * Function * Object * Boolean * Error * Number * BigInt * Math * Date * String * RegExp * Array * Map * Set * Promise * Intl ## Sovraccarichi Gli overload di funzione in TypeScript consentono di definire più firme di funzione per un singolo nome di funzione, consentendo di definire funzioni che possono essere chiamate in più modi. Ecco un esempio: ```typescript // Sovraccarichi function sayHi(name: string): string; function sayHi(names: string[]): string[]; // Implementazione function sayHi(name: unknown): unknown { if (typeof name === 'string') { return `Ciao, ${name}!`; } else if (Array.isArray(name)) { return name.map(name => `Ciao, ${name}!`); } throw new Error('Valore non valido'); } sayHi('xx'); // Valido sayHi(['aa', 'bb']); // Valido ``` Ecco un altro esempio di utilizzo di overload di funzione all'interno di una `class`: ```typescript class Greeter { message: string; constructor(message: string) { this.message = message; } // Sovraccarichi sayHi(name: string): string; sayHi(name: string[]): ReadonlyArray; // Implementazione sayHi(name: unknown): unknown { if (typeof name === 'string') { return `${this.message}, ${name}!`; } else if (Array.isArray(name)) { return name.map(name => `${this.message}, ${name}!`); } throw new Error('value is invalid'); } } console.log(new Greeter('Hello').sayHi('Simon')); ``` ## Unione ed estensione Merging ed estensione si riferiscono a due concetti diversi relativi all'utilizzo di tipi e interfacce. Merging consente di combinare più dichiarazioni con lo stesso nome in un'unica definizione, ad esempio quando si definisce un'interfaccia con lo stesso nome più volte: ```typescript interface X { a: string; } interface X { b: number; } const person: X = { a: 'a', b: 7, }; ``` L'estensione si riferisce alla possibilità di estendere o ereditare da tipi o interfacce esistenti per crearne di nuovi. È un meccanismo per aggiungere proprietà o metodi aggiuntivi a un tipo esistente senza modificarne la definizione originale. Esempio: ```typescript interface Animal { name: string; eat(): void; } interface Bird extends Animal { sing(): void; } const dog: Bird = { name: 'Bird 1', eat() { console.log('Eating'); }, sing() { console.log('Singing'); }, }; ``` ## Differenze tra tipo e interfaccia Unione delle dichiarazioni (aumento): Le interfacce supportano l'unione delle dichiarazioni, il che significa che è possibile definire più interfacce con lo stesso nome e TypeScript le unirà in un'unica interfaccia con le proprietà e i metodi combinati. D'altra parte, i tipi non supportano l'unione delle dichiarazioni. Questo può essere utile quando si desidera aggiungere funzionalità extra o personalizzare i tipi esistenti senza modificare le definizioni originali o correggere tipi mancanti o errati. ```typescript interface A { x: string; } interface A { y: string; } const j: A = { x: 'xx', y: 'yy', }; ``` Estensione di altri tipi/interfacce: Sia i tipi che le interfacce possono estendere altri tipi/interfacce, ma la sintassi è diversa. Con le interfacce, si utilizza la parola chiave `extends` per ereditare proprietà e metodi da altre interfacce. Tuttavia, un'interfaccia non può estendere un tipo complesso come un tipo unione. ```typescript interface A { x: string; y: number; } interface B extends A { z: string; } const car: B = { x: 'x', y: 123, z: 'z', }; ``` Per i tipi, si utilizza l'operatore & per combinare più tipi in un unico tipo (intersezione). ```typescript interface A { x: string; y: number; } type B = A & { j: string; }; const c: B = { x: 'x', y: 123, j: 'j', }; ``` Tipi di unione e intersezione: I tipi sono più flessibili quando si tratta di definire tipi di unione e intersezione. Con la parola chiave `type`, è possibile creare facilmente tipi di unione utilizzando l'operatore `|` e tipi di intersezione utilizzando l'operatore `&`. Sebbene le interfacce possano anche rappresentare tipi di unione indirettamente, non dispongono di supporto integrato per i tipi di intersezione. ```typescript type Department = 'dep-x' | 'dep-y'; // Unione type Person = { name: string; age: number; }; type Employee = { id: number; department: Department; }; type EmployeeInfo = Person & Employee; // Intersezione ``` Esempio con interfacce: ```typescript interface A { x: 'x'; } interface B { y: 'y'; } type C = A | B; // Unione di interfacce ``` ## Classe ### Sintassi comune della classe La parola chiave `class` viene utilizzata in TypeScript per definire una classe. Di seguito è riportato un esempio: ```typescript class Person { private name: string; private age: number; constructor(name: string, age: number) { this.name = name; this.age = age; } public sayHi(): void { console.log(`Ciao, mi chiamo ${this.name} e ho ${this.age} anni.`); } } ``` La parola chiave `class` viene utilizzata per definire una classe denominata "Person". La classe ha due proprietà private: name di tipo `string` ed age di tipo `number`. Il costruttore viene definito utilizzando la parola chiave `constructor`. Accetta name ed age come parametri e li assegna alle proprietà corrispondenti. La classe ha un metodo `public` denominato sayHi che registra un messaggio di saluto. Per creare un'istanza di una classe in TypeScript, è possibile utilizzare la parola chiave `new` seguita dal nome della classe, seguito da parentesi `()`. Ad esempio: ```typescript const myObject = new Person('John Doe', 25); myObject.sayHi(); // Output: Ciao, mi chiamo John Doe e ho 25 anni. ``` ### Costruttore I costruttori sono metodi speciali all'interno di una classe che vengono utilizzati per inizializzare le proprietà dell'oggetto quando viene creata un'istanza della classe. ```typescript class Person { public name: string; public age: number; constructor(name: string, age: number) { this.name = name; this.age = age; } sayHello() { console.log(`Ciao, mi chiamo ${this.name} e ho ${this.age} anni.`); } } const john = new Person('Simon', 17); john.sayHello(); ``` È possibile sovraccaricare un costruttore utilizzando la seguente sintassi: ```typescript type Sex = 'm' | 'f'; class Person { name: string; age: number; sex: Sex; constructor(name: string, age: number, sex?: Sex); constructor(name: string, age: number, sex: Sex) { this.name = name; this.age = age; this.sex = sex ?? 'm'; } } const p1 = new Person('Simon', 17); const p2 = new Person('Alice', 22, 'f'); ``` In TypeScript, è possibile definire più overload del costruttore, ma è possibile avere una sola implementazione che deve essere compatibile con tutti gli overload; questo si può ottenere utilizzando un parametro opzionale. ```typescript class Person { name: string; age: number; constructor(); constructor(name: string); constructor(name: string, age: number); constructor(name?: string, age?: number) { this.name = name ?? 'Sconosciuto'; this.age = age ?? 0; } displayInfo() { console.log(`Nome: ${this.name}, Età: ${this.age}`); } } const person1 = new Person(); person1.displayInfo(); // Nome: unknown, Età: 0 const person2 = new Person('John'); person2.displayInfo(); // Nome: John, Età: 0 const person3 = new Person('Jane', 25); person3.displayInfo(); // Nome: Jane, Età: 25 ``` ### Costruttori privati ​​e protetti In TypeScript, i costruttori possono essere contrassegnati come privati ​​o protetti, il che ne limita l'accessibilità e l'utilizzo. Costruttori privati: possono essere chiamati solo all'interno della classe stessa. I costruttori privati ​​vengono spesso utilizzati in scenari in cui si desidera applicare un pattern singleton o limitare la creazione di istanze a un metodo factory all'interno della classe. Costruttori protetti: I costruttori protetti sono utili quando si desidera creare una classe base che non deve essere istanziata direttamente, ma può essere estesa tramite sottoclassi. ```typescript class BaseClass { protected constructor() {} } class DerivedClass extends BaseClass { private value: number; constructor(value: number) { super(); this.value = value; } } // Il tentativo di istanziare direttamente la classe base genererà un errore. // const baseObj = new BaseClass(); // Errore: il costruttore della classe 'BaseClass' è protetto. // Crea un'istanza della classe derivata const derivedObj = new DerivedClass(10); ``` ### Modificatori di accesso I modificatori di accesso `private`, `protected` e `public` vengono utilizzati per controllare la visibilità e l'accessibilità dei membri della classe, come proprietà e metodi, nelle classi TypeScript. Questi modificatori sono essenziali per applicare l'incapsulamento e stabilire limiti per l'accesso e la modifica dello stato interno di una classe. Il modificatore `private` limita l'accesso al membro della classe solo all'interno della classe contenitore. Il modificatore `protected` consente l'accesso al membro della classe all'interno della classe contenitore e delle sue classi derivate. Il modificatore `public` fornisce accesso illimitato al membro della classe, consentendone l'accesso da qualsiasi luogo. ### Get e Set Getter e setter sono metodi speciali che consentono di definire un comportamento personalizzato di accesso e modifica per le proprietà della classe. Consentono di incapsulare lo stato interno di un oggetto e di fornire logica aggiuntiva durante l'ottenimento o l'impostazione dei valori delle proprietà. In TypeScript, getter e setter sono definiti utilizzando rispettivamente le parole chiave `get` e `set`. Ecco un esempio: ```typescript class MyClass { private _myProperty: string; constructor(value: string) { this._myProperty = value; } get myProperty(): string { return this._myProperty; } set myProperty(value: string) { this._myProperty = value; } } ``` ### Accessori automatici nelle classi TypeScript versione 4.9 aggiunge il supporto per auto-accessor, una funzionalità ECMAScript di prossima uscita. Assomigliano alle proprietà di classe, ma sono dichiarate con la parola chiave "accessor". ```typescript class Animal { accessor name: string; constructor(name: string) { this.name = name; } } ``` Gli auto-accessor vengono "de-sugared" in accessor privati ​​`get` e `set`, che operano su una proprietà inaccessibile. ```typescript class Animal { #__name: string; get name() { return this.#__name; } set name(value: string) { this.#__name = name; } constructor(name: string) { this.name = name; } } ``` ### this In TypeScript, la parola chiave `this` si riferisce all'istanza corrente di una classe all'interno dei suoi metodi o costruttori. Permette di accedere e modificare le proprietà e i metodi della classe. dall'interno del proprio ambito. Fornisce un modo per accedere e manipolare lo stato interno di un oggetto all'interno dei propri metodi. ```typescript class Person { private name: string; constructor(name: string) { this.name = name; } public introduce(): void { console.log(`Ciao, mi chiamo ${this.name}.`); } } const person1 = new Person('Alice'); person1.introduce(); // Ciao, mi chiamo Alice. ``` ### Proprietà dei parametri Le proprietà dei parametri consentono di dichiarare e inizializzare le proprietà della classe direttamente all'interno dei parametri del costruttore, evitando codice boilerplate, ad esempio: ```typescript class Person { constructor( private name: string, public age: number ) { // Le parole chiave "private" e "public" nel costruttore // dichiarano e inizializzano automaticamente le proprietà della classe corrispondenti. } public introduce(): void { console.log(`Ciao, mi chiamo ${this.name} e ho ${this.age} anni.`); } } const person = new Person('Alice', 25); person.introduce(); ``` ### Classi astratte Le classi astratte sono utilizzate in TypeScript principalmente per l'ereditarietà, poiché forniscono un modo per definire proprietà e metodi comuni che possono essere ereditati dalle sottoclassi. Questo è utile quando si desidera definire un comportamento comune e imporre alle sottoclassi di implementare determinati metodi. Forniscono un modo per creare una gerarchia di classi in cui la classe base astratta fornisce un'interfaccia condivisa e funzionalità comuni per le sottoclassi. ```typescript abstract class Animal { protected name: string; constructor(name: string) { this.name = name; } abstract makeSound(): void; } class Cat extends Animal { makeSound(): void { console.log(`${this.name} miagola.`); } } const cat = new Cat('Whiskers'); cat.makeSound(); // Output: Whiskers miagola. ``` ### Con i generici Le classi con i generici consentono di definire classi riutilizzabili che possono funzionare con tipi diversi. ```typescript class Container { private item: T; constructor(item: T) { this.item = item; } getItem(): T { return this.item; } setItem(item: T): void { this.item = item; } } const container1 = new Container(42); console.log(container1.getItem()); // 42 const container2 = new Container('Hello'); container2.setItem('World'); console.log(container2.getItem()); // Mondo ``` ### Decoratori I decoratori forniscono un meccanismo per aggiungere metadati, modificare il comportamento, convalidare o estendere la funzionalità dell'elemento di destinazione. Sono funzioni che vengono eseguite in fase di esecuzione. È possibile applicare più decoratori a una dichiarazione. I decoratori sono funzionalità sperimentali e gli esempi seguenti sono compatibili solo con TypeScript versione 5 o successive che utilizzano ES6. Per le versioni di TypeScript precedenti alla 5, dovrebbero essere abilitati utilizzando la proprietà `experimentalDecorators` nel file `tsconfig.json` o utilizzando `--experimentalDecorators` nella riga di comando (ma l'esempio seguente non funzionerà). Alcuni dei casi d'uso comuni per i decoratori includono: * Monitoraggio delle modifiche delle proprietà. * Monitoraggio delle chiamate ai metodi. * Aggiunta di proprietà o metodi aggiuntivi. * Validazione in fase di esecuzione. * Serializzazione e deserializzazione automatica. * Registrazione. * Autorizzazione e autenticazione. * Protezione dagli errori. Nota: i decoratori per la versione 5 non consentono parametri di decorazione. Tipi di decoratori: #### Decoratori di classe I decoratori di classe sono utili per estendere una classe esistente, ad esempio aggiungendo proprietà o metodi o raccogliendo istanze di una classe. Nell'esempio seguente, aggiungiamo un metodo `toString` che converte la classe in una rappresentazione in formato stringa. ```typescript type Constructor = new (...args: any[]) => T; function toString( Value: Class, context: ClassDecoratorContext ) { return class extends Value { constructor(...args: any[]) { super(...args); console.log(JSON.stringify(this)); console.log(JSON.stringify(context)); } }; } @toString class Person { name: string; constructor(name: string) { this.name = name; } greet() { return 'Ciao,' + this.name; } } const person = new Person('Simon'); /* Log: {"name":"Simon"} {"kind":"class","name":"Person"} */ ``` #### Decoratore di proprietà I decoratori di proprietà sono utili per modificare il comportamento di una proprietà, ad esempio cambiando i valori di inizializzazione. Nel codice seguente, abbiamo uno script che imposta una proprietà in modo che sia sempre in maiuscolo: ```typescript function upperCase( target: undefined, context: ClassFieldDecoratorContext ) { return function (this: T, value: string) { return value.toUpperCase(); }; } class MyClass { @upperCase prop1 = 'hello!'; } console.log(new MyClass().prop1); // Log: HELLO! ``` #### Decoratore di metodo I decoratori di metodo consentono di modificare o migliorare il comportamento dei metodi. Di seguito è riportato un esempio di un semplice logger: ```typescript function log( target: (this: This, ...args: Args) => Return, context: ClassMethodDecoratorContext< This, (this: This, ...args: Args) => Return > ) { const methodName = String(context.name); function replacementMethod(this: This, ...args: Args): Return { console.log(`LOG: Accesso al metodo '${methodName}'.`); const result = target.call(this, ...args); console.log(`LOG: Uscita dal metodo '${methodName}'.`); return result; } return replacementMethod; } class MyClass { @log sayHello() { console.log('Ciao!'); } } new MyClass().sayHello(); ``` Registra: ```shell LOG: Accesso al metodo 'sayHello'. Ciao! LOG: Uscita dal metodo 'sayHello'. ``` #### Decoratori Getter e Setter I decoratori Getter e Setter consentono di modificare o migliorare il comportamento degli accessor di classe. Sono utili, ad esempio, per convalidare le assegnazioni di proprietà. Ecco un semplice esempio di decoratore getter: ```typescript function range(min: number, max: number) { return function ( target: (this: This) => Return, context: ClassGetterDecoratorContext ) { return function (this: This): Return { const value = target.call(this); if (value < min || value > max) { throw 'Invalid'; } Object.defineProperty(this, context.name, { value, enumerable: true, }); return value; }; }; } class MyClass { private _value = 0; constructor(value: number) { this._value = value; } @range(1, 100) get getValue(): number { return this._value; } } const obj = new MyClass(10); console.log(obj.getValue); // Valido: 10 const obj2 = new MyClass(999); console.log(obj2.getValue); // Throw: Invalid! ``` #### Metadati del decoratore I metadati del decoratore semplificano il processo per i decoratori di applicare e utilizzare i metadati in qualsiasi classe. Possono accedere a una nuova proprietà metadati sull'oggetto contesto, che può fungere da chiave sia per le primitive che per gli oggetti. Le informazioni sui metadati sono accessibili sulla classe tramite `Symbol.metadata`. I metadati possono essere utilizzati per vari scopi, come il debug, la serializzazione o l'iniezione di dipendenze con i decoratori. ```typescript //@ts-ignore Symbol.metadata ??= Symbol('Symbol.metadata'); // Simple polyfill type Context = | ClassFieldDecoratorContext | ClassAccessorDecoratorContext | ClassMethodDecoratorContext; // Il contesto contiene la proprietà metadati: DecoratorMetadata function setMetadata(_target: any, context: Context) { // Imposta l'oggetto metadati con un valore primitivo context.metadata[context.name] = true; } class MyClass { @setMetadata a = 123; @setMetadata accessor b = 'b'; @setMetadata fn() {} } const metadata = MyClass[Symbol.metadata]; // Ottieni informazioni sui metadati console.log(JSON.stringify(metadata)); // {"bar":true,"baz":true,"foo":true} ``` ### Ereditarietà L'ereditarietà si riferisce al meccanismo mediante il quale una classe può ereditare proprietà e metodi da un'altra classe, nota come classe base o superclasse. La classe derivata, chiamata anche classe figlia o sottoclasse, può estendere e specializzare le funzionalità della classe base aggiungendo nuove proprietà e metodi o sovrascrivendo quelli esistenti. ```typescript class Animal { name: string; constructor(name: string) { this.name = name; } speak(): void { console.log("L'animale emette un suono"); } } class Dog extends Animal { breed: string; constructor(name: string, breed: string) { super(name); this.breed = breed; } speak(): void { console.log('Woof! Woof!'); } } // Crea un'istanza della classe base const animal = new Animal('Animale generico'); animal.speak(); // L'animale emette un suono // Crea un'istanza della classe derivata const dog = new Dog('Max', 'Labrador'); dog.speak(); // Woof! Bau!" ``` TypeScript non supporta l'ereditarietà multipla nel senso tradizionale, ma consente invece l'ereditarietà da una singola classe base. TypeScript supporta più interfacce. Un'interfaccia può definire un contratto per la struttura di un oggetto e una classe può implementare più interfacce. Questo consente a una classe di ereditare comportamento e struttura da più fonti. ```typescript interface Flyable { fly(): void; } interface Swimmable { swim(): void; } class FlyingFish implements Flyable, Swimmable { fly() { console.log('Flying...'); } swim() { console.log('Swimming...'); } } const flyingFish = new FlyingFish(); flyingFish.fly(); flyingFish.swim(); ``` La parola chiave `class` in TypeScript, simile a JavaScript, è spesso definita "syntactic sugar". È stata introdotta in ECMAScript 2015 (ES6) offre una sintassi più familiare per la creazione e l'utilizzo di oggetti in modalità basata sulle classi. Tuttavia, è importante notare che TypeScript, essendo un superset di JavaScript, alla fine si compila in JavaScript, che rimane fondamentalmente basato sui prototipi. ### Statiche TypeScript ha membri statici. Per accedere ai membri statici di una classe, è possibile utilizzare il nome della classe seguito da un punto, senza dover creare un oggetto. ```typescript class OfficeWorker { static memberCount: number = 0; constructor(private name: string) { OfficeWorker.memberCount++; } } const w1 = new OfficeWorker('James'); const w2 = new OfficeWorker('Simon'); const total = OfficeWorker.memberCount; console.log(total); // 2 ``` ### Inizializzazione delle proprietà Esistono diversi modi per Inizializza le proprietà per una classe in TypeScript: Inline: Nell'esempio seguente, questi valori iniziali verranno utilizzati quando verrà creata un'istanza della classe. ```typescript class MyClass { property1: string = 'default value'; property2: number = 42; } ``` Nel costruttore: ```typescript class MyClass { property1: string; property2: number; constructor() { this.property1 = 'default value'; this.property2 = 42; } } ``` Utilizzo dei parametri del costruttore: ```typescript class MyClass { constructor( private property1: string = 'default value', public property2: number = 42 ) { // Non è necessario assegnare esplicitamente i valori alle proprietà. } log() { console.log(this.property2); } } const x = new MyClass(); x.log(); ``` ### Sovraccarico dei metodi Il sovraccarico dei metodi consente a una classe di avere più metodi con lo stesso nome ma tipi di parametri diversi o un numero diverso di parametri. Questo ci permette di chiamare un metodo in modi diversi in base agli argomenti passati. ```typescript class MyClass { add(a: number, b: number): number; // Firma di sovraccarico 1 add(a: string, b: string): string; // Firma di sovraccarico 2 add(a: number | string, b: number | string): number | string { if (typeof a === 'number' && typeof b === 'number') { return a + b; } if (typeof a === 'string' && typeof b === 'string') { return a.concat(b); } throw new Error('Argomenti non validi'); } } const r = new MyClass(); console.log(r.add(10, 5)); // Log 15 ``` ## Generici I generici consentono di creare componenti e funzioni riutilizzabili che possono funzionare con più tipi. Con i generici, è possibile parametrizzare tipi, funzioni e interfacce, consentendo loro di operare su tipi diversi senza doverli specificare esplicitamente in anticipo. I generici consentono di rendere il codice più flessibile e riutilizzabile. ### Tipo generico Per definire un tipo generico, si utilizzano le parentesi angolari (`<>`) per specificare i parametri di tipo, ad esempio: ```typescript function identity(arg: T): T { return arg; } const a = identity('x'); const b = identity(123); const getLen = (data: ReadonlyArray) => data.length; const len = getLen([1, 2, 3]); ``` ### Classi generiche I generici possono essere applicati anche alle classi, in questo modo possono lavorare con più tipi utilizzando parametri di tipo. Questo è utile per creare definizioni di classe riutilizzabili che possono operare su diversi tipi di dati mantenendo la sicurezza dei tipi. ```typescript class Container { private item: T; constructor(item: T) { this.item = item; } getItem(): T { return this.item; } } const numberContainer = new Container(123); console.log(numberContainer.getItem()); // 123 const stringContainer = new Container('hello'); console.log(stringContainer.getItem()); // ciao ``` ### Vincoli generici I parametri generici possono essere vincolati utilizzando la parola chiave `extends` seguita da un tipo o un'interfaccia che il parametro di tipo deve soddisfare. Nell'esempio seguente, T deve contenere una `length` appropriata per essere valido: ```typescript const printLen = (value: T): void => { console.log(value.length); }; printLen('Ciao'); // 5 printLen([1, 2, 3]); // 3 printLen({ length: 10 }); // 10 printLen(123); // Non valido ``` Una caratteristica interessante di generic introdotta nella versione 3.4 RC è l'inferenza di tipo di funzione di ordine superiore, che ha introdotto argomenti di tipo generico propagati: ```typescript declare function pipe( ab: (...args: A) => B, bc: (b: B) => C ): (...args: A) => C; declare function list(a: T): T[]; declare function box(x: V): { value: V }; const listBox = pipe(list, box); // (a: T) => { value: T[] } const boxList = pipe(box, list); // (x: V) => { value: V }[] ``` Questa funzionalità consente una programmazione più semplice, sicura e senza punti, comune nella programmazione funzionale. ### Restringimento contestuale generico Il restringimento contestuale per i generici è il meccanismo di TypeScript che consente al compilatore di restringere il tipo di un parametro generico in base al contesto in cui viene utilizzato. È utile quando si lavora con tipi generici in istruzioni condizionali: ```typescript function process(value: T): void { if (typeof value === 'string') { // Il valore viene ristretto al tipo 'string' console.log(value.length); } else if (typeof value === 'number') { // Il valore viene ristretto al tipo 'number' console.log(value.toFixed(2)); } } process('hello'); // 5 process(3.14159); // 3.14 ``` ## Tipi strutturali cancellati In TypeScript, gli oggetti non devono necessariamente corrispondere a un tipo specifico ed esatto. Ad esempio, se creiamo un oggetto che soddisfa i requisiti di un'interfaccia, possiamo utilizzare quell'oggetto nei punti in cui l'interfaccia è richiesta, anche se non esiste una connessione esplicita tra i due. Esempio: ```typescript type NameProp1 = { prop1: string; }; function log(x: NameProp1) { console.log(x.prop1); } const obj = { prop2: 123, prop1: 'Origin', }; log(obj); // Valido ``` ## Namespace In TypeScript, gli spazi dei nomi vengono utilizzati per organizzare il codice in contenitori logici, prevenendo collisioni di nomi e fornendo un modo per raggruppare il codice correlato. L'utilizzo delle parole chiave `export` consente l'accesso allo spazio dei nomi nei moduli "esterni". ```typescript export namespace MyNamespace { export interface MyInterface1 { prop1: boolean; } export interface MyInterface2 { prop2: string; } } const a: MyNamespace.MyInterface1 = { prop1: true, }; ``` ## Simboli I simboli sono un tipo di dati primitivo che rappresenta un valore immutabile la cui unicità globale è garantita per tutta la durata del programma. I simboli possono essere utilizzati come chiavi per le proprietà degli oggetti e forniscono un modo per creare proprietà non enumerabili. ```typescript const key1: symbol = Symbol('key1'); const key2: symbol = Symbol('key2'); const obj = { [key1]: 'value 1', [key2]: 'value 2', }; console.log(obj[key1]); // valore 1 console.log(obj[key2]); // valore 2 ``` In WeakMaps e WeakSet, i simboli sono ora consentiti come chiavi. ## Direttive con tripla barra Le direttive con tripla barra sono commenti speciali che forniscono istruzioni al compilatore su come elaborare un file. Queste direttive iniziano con tre barre consecutive (`///`) e sono in genere posizionate all'inizio di un file TypeScript e non hanno alcun effetto sul comportamento in fase di esecuzione. Le direttive con tripla barra vengono utilizzate per fare riferimento a dipendenze esterne, specificare il comportamento di caricamento dei moduli, abilitare/disabilitare determinate funzionalità del compilatore e altro ancora. Alcuni esempi: Riferimento a un file di dichiarazione: ```typescript /// ``` Indicare il formato del modulo: ```typescript /// ``` Abilitare le opzioni del compilatore, nell'esempio seguente, in modalità strict: ```typescript /// ``` ## Manipolazione dei tipi ### Creazione di tipi da tipi È possibile creare nuovi tipi componendo, manipolando o trasformando tipi esistenti. Tipi di intersezione (`&`): Consentono di combinare più tipi in un unico tipo: ```typescript type A = { foo: number }; type B = { bar: string }; type C = A & B; // Intersezione di A e B const obj: C = { foo: 42, bar: 'hello' }; ``` Tipi di unione (`|`): Consente di definire un tipo che può essere di diversi tipi: ```typescript type Result = string | number; const value1: Result = 'hello'; const value2: Result = 42; ``` Tipi mappati: Consentono di trasformare le proprietà di un tipo esistente per crearne uno nuovo: ```typescript type Mutable = { readonly [P in keyof T]: T[P]; }; type Person = { name: string; age: number; }; type ImmutablePerson = Mutable; // Le proprietà diventano di sola lettura ``` Tipi condizionali: Consentono di creare tipi in base ad alcune condizioni: ```typescript type ExtractParam = T extends (param: infer P) => any ? P : never; type MyFunction = (name: string) => number; type ParamType = ExtractParam; // string ``` ### Tipi di accesso indicizzati In TypeScript è possibile accedere e manipolare i tipi di proprietà all'interno di un altro tipo utilizzando un indice, `Type[Key]`. ```typescript type Person = { name: string; age: number; }; type AgeType = Person['age']; // number ``` ```typescript type MyTuple = [string, number, boolean]; type MyType = MyTuple[2]; // boolean ``` ### Tipi di utilità Diversi tipi di utilità predefiniti possono essere utilizzati per manipolare i tipi; di seguito è riportato un elenco dei più comuni: #### Awaited\ Costruisce un tipo che esegue ricorsivamente l'unwrapping dei tipi Promise. ```typescript type A = Awaited>; // string ``` #### Partial\ Costruisce un tipo con tutte le proprietà di T impostate su optional. ```typescript type Person = { name: string; age: number; }; type A = Partial; // { name?: string | undefined; age?:number | undefined; } ``` #### Required\ Costruisce un tipo con tutte le proprietà di T impostate su required. ```typescript type Person = { name?: string; age?: number; }; type A = Required; // { name: string; age:number; } ``` #### Readonly\ Costruisce un tipo con tutte le proprietà di T impostate su readonly. ```typescript type Person = { name: string; age: number; }; type A = Readonly; const a: A = { name: 'Simon', age: 17 }; a.name = 'John'; // Non valido ``` #### Record\ Costruisce un tipo con un insieme di proprietà K di tipo T. ```typescript type Product = { name: string; price: number; }; const products: Record = { apple: { name: 'Apple', price: 0.5 }, banana: { name: 'Banana', price: 0.25 }, }; console.log(products.apple); // { name: 'Apple', price: 0.5 } ``` #### Pick\ Costruisce un tipo selezionando le proprietà specificate K da T. ```typescript type Product = { name: string; price: number; }; type Price = Pick; // { price: number; } ``` #### Omit\ Costruisce un tipo omettendo le proprietà specificate K da T. ```typescript type Product = { name: string; price: number; }; type Name = Omit; // { name: string; } ``` #### Exclude\ Costruisce un tipo escludendo tutti i valori di tipo U da T. ```typescript type Union = 'a' | 'b' | 'c'; type MyType = Exclude; // b ``` #### Extract\ Costruisce un tipo estraendo tutti i valori di tipo U da T. ```typescript type Union = 'a' | 'b' | 'c'; type MyType = Extract; // a | c ``` #### NonNullable\ Costruisce un tipo escludendo null e undefined da T. ```typescript type Union = 'a' | null | undefined | 'b'; type MyType = NonNullable; // 'a' | 'b' ``` #### Parameters\ Estrae i tipi di parametro di una funzione di tipo T. ```typescript type Func = (a: string, b: number) => void; type MyType = Parameters; // [a: string, b: number] ``` #### ConstructorParameters\ Estrae i tipi di parametro di una funzione costruttore di tipo T. ```typescript class Person { constructor( public name: string, public age: number ) {} } type PersonConstructorParams = ConstructorParameters; // [name: string, age: number] const params: PersonConstructorParams = ['John', 30]; const person = new Person(...params); console.log(person); // Person { name: 'John', age: 30 } ``` #### ReturnType\ Estrae il tipo di ritorno di una funzione di tipo T. ```typescript type Func = (name: string) => number; type MyType = ReturnType; // number ``` #### InstanceType\ Estrae il tipo di istanza di una classe di tipo T. ```typescript class Person { name: string; constructor(name: string) { this.name = name; } sayHello() { console.log(`Ciao, mi chiamo ${this.name}!`); } } type PersonInstance = InstanceType; const person: PersonInstance = new Person('John'); person.sayHello(); // Ciao, mi chiamo John! ``` #### ThisParameterType\ Estrae il tipo del parametro 'this' da una funzione di tipo T. ```typescript interface Person { name: string; greet(this: Person): void; } type PersonThisType = ThisParameterType; // Person ``` #### OmitThisParameter\ Rimuove il parametro 'this' da una funzione di tipo T. ```typescript function capitalize(this: String) { return this[0].toUpperCase + this.substring(1).toLowerCase(); } type CapitalizeType = OmitThisParameter; // () => string ``` #### ThisType\ Funge da marcatore per un tipo `this` contestuale. ```typescript type Logger = { log: (error: string) => void; }; let helperFunctions: { [name: string]: Function } & ThisType = { hello: function () { this.log('some error'); // Valido poiché "log" è parte di "this". this.update(); // Non valido }, }; ``` #### Uppercase\ Rendi maiuscolo il nome del tipo di input T. ```typescript type MyType = Uppercase<'abc'>; // "ABC" ``` #### Lowercase\ Rendi minuscolo il nome del tipo di input T. ```typescript type MyType = Lowercase<'ABC'>; // "abc" ``` #### Capitalize\ Inserisci in maiuscolo il nome del tipo di input T. ```typescript type MyType = Capitalize<'abc'>; // "Abc" ``` #### Uncapitalize\ Inserisci in maiuscolo il nome del tipo di input T. ```typescript type MyType = Uncapitalize<'Abc'>; // "abc" ``` #### NoInfer\ NoInfer è un tipo di utilità progettato per bloccare l'inferenza automatica dei tipi nell'ambito di una funzione generica. Esempio: ```typescript // Inferenza automatica dei tipi nell'ambito di una funzione generica. function fn(x: T[], y: T) { return x.concat(y); } const r = fn(['a', 'b'], 'c'); // Il tipo qui è ("a" | "b" | "c")[] ``` Con NoInfer: ```typescript // Funzione di esempio che utilizza NoInfer per impedire l'inferenza di tipo function fn2(x: T[], y: NoInfer) { return x.concat(y); } const r2 = fn2(['a', 'b'], 'c'); // Errore: l'argomento di tipo '"c"' non è assegnabile al parametro di tipo '"a" | "b"'. ``` ## Altri ### Gestione degli errori e delle eccezioni TypeScript consente di rilevare e gestire gli errori utilizzando i meccanismi standard di gestione degli errori JavaScript: Blocchi Try-Catch-Finally: ```typescript try { // Codice che potrebbe generare un errore } catch (error) { // Gestisci l'errore } finally { // Codice che viene sempre eseguito, finally è facoltativo } ``` È anche possibile gestire diversi tipi di errore: ```typescript try { // Codice che potrebbe generare diversi tipi di errore } catch (error) { if (error instanceof TypeError) { // Gestisci TypeError } else if (error instanceof RangeError) { // Gestisci RangeError } else { // Gestisci altri errori } } ``` Tipi di errore personalizzati: È possibile specificare errori più specifici estendendo la classe `Error`: ```typescript class CustomError extends Error { constructor(message: string) { super(message); this.name = 'CustomError'; } } throw new CustomError('Questo è un errore personalizzato.'); ``` ### Classi Mixin Le classi Mixin consentono di combinare e comporre il comportamento di più classi in un'unica classe. Forniscono un modo per riutilizzare ed estendere le funzionalità senza la necessità di catene di ereditarietà profonde. ```typescript abstract class Identificabile { name: string = ''; logId() { console.log('id:', this.name); } } abstract class Selezionabile { selected: boolean = false; select() { this.selected = true; console.log('Select'); } deselect() { this.selected = false; console.log('Deselect'); } } class MyClass { constructor() {} } // Estendi MyClass per includere il comportamento di Identificabile e Selezionabile interface MyClass extends Identificabile, Selezionabile {} // Funzione per applicare i mixin a una classe function applyMixins(source: any, baseCtors: any[]) { baseCtors.forEach(baseCtor => { Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => { const descriptor = Object.getOwnPropertyDescriptor( baseCtor.prototype, name ); if (descriptor) { Object.defineProperty(source.prototype, name, descriptor); } }); }); } // Applica i mixin a MyClass applyMixins(MyClass, [Identificabile, Selezionabile]); let o = new MyClass(); o.name = 'abc'; o.logId(); o.select(); ``` ### Funzionalità del linguaggio asincrono Essendo TypeScript un superset di JavaScript, integra funzionalità del linguaggio asincrono come: Promise: Le promise sono un modo per gestire le operazioni asincrone e i loro risultati utilizzando metodi come `.then()` e `.catch()` per gestire le condizioni di successo e di errore. Per saperne di più: [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) Async/await: Le parole chiave Async/await sono un modo per fornire una sintassi più sincrona per lavorare con le promise. La parola chiave `async` viene utilizzata per definire una funzione asincrona, mentre la parola chiave `await` viene utilizzata all'interno di una funzione asincrona per mettere in pausa l'esecuzione finché una Promise non viene risolta o rifiutata. Per saperne di più: [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function) [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await) Le seguenti API sono ben supportate in TypeScript: API Fetch: [https://developer.mozilla.org/it/docs/Web/API/Fetch_API](https://developer.mozilla.org/it/docs/Web/API/Fetch_API) Web Worker: [https://developer.mozilla.org/it/docs/Web/API/Web_Workers_API](https://developer.mozilla.org/it/docs/Web/API/Web_Workers_API) Condiviso Worker: [https://developer.mozilla.org/en-US/docs/Web/API/SharedWorker](https://developer.mozilla.org/en-US/docs/Web/API/SharedWorker) WebSocket: [https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API) ### Iteratori e Generatori Sia gli Iteratori che i Generatori sono ben supportati in TypeScript. Gli Iteratori sono oggetti che implementano il protocollo Iterator, fornendo un modo per accedere agli elementi di una collezione o sequenza uno alla volta. Si tratta di una struttura che contiene un puntatore all'elemento successivo nell'iterazione. Hanno un metodo `next()` che restituisce il valore successivo nella sequenza insieme a un valore booleano che indica se la sequenza è `completata`. ```typescript class NumberIterator implements Iterable { private current: number; constructor( private start: number, private end: number ) { this.current = start; } public next(): IteratorResult { if (this.current <= this.end) { const value = this.current; this.current++; return { value, done: false }; } else { return { value: undefined, done: true }; } } [Symbol.iterator](): Iterator { return this; } } const iterator = new NumberIterator(1, 3); for (const num of iterator) { console.log(num); } ``` I generatori sono funzioni speciali definite utilizzando la sintassi `function*` che semplifica la creazione di iteratori. Utilizzano la parola chiave `yield` per definire la sequenza di valori e mettono automaticamente in pausa e riprendono l'esecuzione quando vengono richiesti valori. I generatori semplificano la creazione di iteratori e sono particolarmente utili per lavorare con sequenze di grandi dimensioni o infinite. Esempio: ```typescript function* numberGenerator(start: number, end: number): Generator { for (let i = start; i <= end; i++) { yield i; } } const generator = numberGenerator(1, 5); for (const num of generator) { console.log(num); } ``` TypeScript supporta anche iteratori e generatori asincroni. Per saperne di più: [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Generator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Generator) [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Iterator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Iterator) ### Riferimento JSDoc di TsDocs Quando si lavora con una base di codice JavaScript, è possibile aiutare TypeScript a dedurre il tipo corretto utilizzando commenti JSDoc con annotazioni aggiuntive per fornire informazioni sul tipo. Esempio: ```typescript /** * Calcola la potenza di un numero dato * @constructor * @param {number} base – Il valore base dell'espressione * @param {number} exponent – ​​Il valore esponente dell'espressione */ function power(base: number, exponent: number) { return Math.pow(base, exponent); } power(10, 2); // function power(base: number, exponent: number): number ``` La documentazione completa è disponibile a questo link: [https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html](https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html) Dalla versione 3.7 è possibile generare definizioni di tipo .d.ts dalla sintassi JavaScript JSDoc. Ulteriori informazioni sono disponibili qui: [https://www.typescriptlang.org/docs/handbook/declaration-files/dts-from-js.html](https://www.typescriptlang.org/docs/handbook/declaration-files/dts-from-js.html) ### @types I pacchetti nell'organizzazione @types sono convenzioni di denominazione speciali utilizzate per fornire definizioni di tipo per librerie o moduli JavaScript esistenti. Ad esempio, usando: ```shell npm install --save-dev @types/lodash ``` Installerà le definizioni di tipo di `lodash` nel tuo progetto corrente. Per contribuire alle definizioni di tipo del pacchetto @types, invia una pull request a [https://github.com/DefinitelyTyped/DefinitelyTyped](https://github.com/DefinitelyTyped/DefinitelyTyped). ### JSX JSX (JavaScript XML) è un'estensione della sintassi del linguaggio JavaScript che consente di scrivere codice simile a HTML all'interno dei file JavaScript o TypeScript. Viene comunemente utilizzato in React per definire la struttura HTML. TypeScript extends le funzionalità di JSX fornendo il controllo dei tipi e l'analisi statica. Per utilizzare JSX è necessario impostare l'opzione del compilatore `jsx` nel file `tsconfig.json`. Due opzioni di configurazione comuni: * "preserve": emette file .jsx con il JSX invariato. Questa opzione indica a TypeScript di mantenere la sintassi JSX così com'è e di non trasformarla durante il processo di compilazione. È possibile utilizzare questa opzione se si dispone di uno strumento separato, come Babel, che gestisce la trasformazione. * "react": abilita la trasformazione JSX integrata di TypeScript. Verrà utilizzato React.createElement. Tutte le opzioni sono disponibili qui: [https://www.typescriptlang.org/tsconfig#jsx](https://www.typescriptlang.org/tsconfig#jsx) ### Moduli ES6 TypeScript supporta ES6 (ECMAScript 2015) e molte versioni successive. Ciò significa che è possibile utilizzare la sintassi ES6, come funzioni freccia, letterali template, classi, moduli, destrutturazione e altro ancora. Per abilitare le funzionalità ES6 nel progetto, è possibile specificare la proprietà `target` nel file tsconfig.json. Un esempio di configurazione: ```json { "compilerOptions": { "target": "es6", "module": "es6", "moduleResolution": "node", "sourceMap": true, "outDir": "dist" }, "include": ["src"] } ``` ### Operatore di elevamento a potenza ES7 L'operatore di elevamento a potenza (`**`) calcola il valore ottenuto elevando il primo operando alla potenza del secondo operando. Funziona in modo simile a `Math.pow()`, ma con la possibilità aggiuntiva di accettare BigInt come operandi. TypeScript supporta pienamente questo operatore, utilizzandolo come `target` nel file tsconfig.json `es2016` o versione successiva. ```typescript console.log(2 ** (2 ** 2)); // 16 ``` ### L'istruzione for-await-of Questa è una funzionalità JavaScript completamente supportata in TypeScript che consente di iterare su oggetti iterabili asincroni dalla versione target es2018. ```typescript async function* asyncNumbers(): AsyncIterableIterator { yield Promise.resolve(1); yield Promise.resolve(2); yield Promise.resolve(3); } (async () => { for await (const num of asyncNumbers()) { console.log(num); } })(); ``` ### Nuova meta-proprietà target In TypeScript è possibile utilizzare la meta-proprietà `new.target`, che consente di determinare se una funzione o un costruttore è stato invocato utilizzando l'operatore new. Permette inoltre di rilevare se un oggetto è stato creato a seguito di una chiamata al costruttore. ```typescript class Parent { constructor() { console.log(new.target); // Registra la funzione costruttore utilizzata per creare un'istanza } } class Child extends Parent { constructor() { super(); } } const parentX = new Parent(); // [Function: Parent] const child = new Child(); // [Function: Child] ``` ### Espressioni di importazione dinamica È possibile caricare i moduli in modo condizionale o caricarli in modo differito su richiesta utilizzando la proposta ECMAScript per l'importazione dinamica, supportata in TypeScript. La sintassi per le espressioni di importazione dinamica in TypeScript è la seguente: ```typescript async function renderWidget() { const container = document.getElementById('widget'); if (container !== null) { const widget = await import('./widget'); // Importazione dinamica widget.render(container); } } renderWidget(); ``` ### "tsc –watch" Questo comando avvia un compilatore TypeScript con il parametro `--watch`, con la possibilità di ricompilare automaticamente i file TypeScript ogni volta che vengono modificati. ```shell tsc --watch ``` A partire dalla versione 4.9 di TypeScript, il monitoraggio dei file si basa principalmente sugli eventi del file system, ricorrendo automaticamente al polling se non è possibile stabilire un watcher basato sugli eventi. ### Operatore di asserzione non nullo L'operatore di asserzione non nullo (Postfix !), noto anche come asserzione di assegnazione definita, è una funzionalità di TypeScript che consente di affermare che una variabile o una proprietà non è nulla o indefinita, anche se l'analisi statica dei tipi di TypeScript suggerisce che potrebbe esserlo. Con questa funzionalità è possibile rimuovere qualsiasi controllo esplicito. ```typescript type Person = { name: string; }; const printName = (person?: Person) => { console.log(`Name is ${person!.name}`); }; ``` ### Dichiarazioni predefinite Le dichiarazioni predefinite vengono utilizzate quando a una variabile o a un parametro viene assegnato un valore predefinito. Ciò significa che se non viene fornito alcun valore per quella variabile o parametro, verrà utilizzato il valore predefinito. ```typescript function greet(name: string = 'Anonymous'): void { console.log(`Ciao, ${name}!`); } greet(); // Ciao, Anonymous! greet('John'); // Ciao, John! ``` ### Concatenamento opzionale L'operatore di concatenamento opzionale `?.` funziona come il normale operatore punto (`.`) per accedere a proprietà o metodi. Tuttavia, gestisce in modo elegante i valori nulli o indefiniti terminando l'espressione e restituendo `undefined`, invece di generare un errore. ```typescript type Person = { name: string; age?: number; address?: { street?: string; city?: string; }; }; const person: Person = { name: 'John', }; console.log(person.address?.city); // indefinito ``` ### Operatore di coalescenza nullo L'operatore di coalescenza nullo `??` restituisce il valore del lato destro se il lato sinistro è `null` o `undefined`; in caso contrario, restituisce il valore del lato sinistro. ```typescript const foo = null ?? 'foo'; console.log(foo); // foo const baz = 1 ?? 'baz'; const baz2 = 0 ?? 'baz'; console.log(baz); // 1 console.log(baz2); // 0 ``` ### Tipi letterali di template I tipi letterali modello consentono di manipolare i valori stringa a livello di tipo e di generare nuovi tipi stringa basati su quelli esistenti. Sono utili per creare tipi più espressivi e precisi da operazioni basate su stringhe. ```typescript type Department = 'engineering' | 'hr'; type Language = 'english' | 'spanish'; type Id = `${Department}-${Language}-id`; // "engineering-english-id" | "engineering-spanish-id" | "hr-english-id" | "hr-spanish-id" ``` ### Sovraccarico di funzioni Il sovraccarico di funzioni consente di definire più firme di funzione per lo stesso nome di funzione, ciascuna con tipi di parametro e tipo di ritorno diversi. Quando si chiama una funzione sovraccaricata, TypeScript utilizza gli argomenti forniti per determinare la firma di funzione corretta: ```typescript function makeGreeting(name: string): string; function makeGreeting(names: string[]): string[]; function makeGreeting(person: unknown): unknown { if (typeof person === 'string') { return `Ciao ${person}!`; } else if (Array.isArray(person)) { return person.map(name => `Ciao, ${name}!`); } throw new Error('Impossibile salutare'); } makeGreeting('Simon'); makeGreeting(['Simone', 'John']); ``` ### Tipi ricorsivi Un tipo ricorsivo è un tipo che può fare riferimento a se stesso. Questo è utile per definire strutture dati che hanno una struttura gerarchica o ricorsiva (annidamento potenzialmente infinito), come liste concatenate, alberi e grafi. ```typescript type ListNode = { data: T; next: ListNode | undefined; }; ``` ### Tipi condizionali ricorsivi È possibile definire relazioni di tipo complesse utilizzando la logica e la ricorsione in TypeScript. Analizziamole in termini semplici: Tipi condizionali: consente di definire tipi in base a condizioni booleane: ```typescript type CheckNumber = T extends number ? 'Number' : 'Not a number'; type A = CheckNumber<123>; // 'Number' type B = CheckNumber<'abc'>; // 'Not a number' ``` Ricorsione: indica una definizione di tipo che fa riferimento a se stessa all'interno della propria definizione: ```typescript type Json = string | number | boolean | null | Json[] | { [key: string]: Json }; const data: Json = { prop1: true, prop2: 'prop2', prop3: { prop4: [], }, }; ``` I tipi condizionali ricorsivi combinano sia la logica condizionale che la ricorsione. Ciò significa che una definizione di tipo può dipendere da se stessa tramite la logica condizionale, creando relazioni di tipo complesse e flessibili. ```typescript type Flatten = T extends Array ? Flatten : T; type NestedArray = [1, [2, [3, 4], 5], 6]; type FlattenedArray = Flatten; // 2 | 3 | 4 | 5 | 1 | 6 ``` ### Supporto per i moduli ECMAScript in Node Node.js ha aggiunto il supporto per i moduli ECMAScript a partire dalla versione 15.3.0, mentre TypeScript supporta i moduli ECMAScript per Node.js dalla versione 4.7. Questo supporto può essere abilitato utilizzando la proprietà `module` con il valore `nodenext` nel file tsconfig.json. Ecco un esempio: ```json { "compilerOptions": { "module": "nodenext", "outDir": "./lib", "declaration": true } } ``` Node.js supporta due estensioni di file per i moduli: `.mjs` per i moduli ES e `.cjs` per i moduli CommonJS. Le estensioni di file equivalenti in TypeScript sono `.mts` per i moduli ES e `.cts` per i moduli CommonJS. Quando il compilatore TypeScript trascrive questi file in JavaScript, creerà i file `.mjs` e `.cjs`. Se desideri utilizzare moduli ES nel tuo progetto, puoi impostare la proprietà `type` su "module" nel file package.json. Questo indica a Node.js di trattare il progetto come un progetto di modulo ES. Inoltre, TypeScript supporta anche le dichiarazioni di tipo nei file .d.ts. Questi file di dichiarazione forniscono informazioni sul tipo per librerie o moduli scritti in TypeScript, consentendo ad altri sviluppatori di utilizzarli con le funzionalità di controllo del tipo e di completamento automatico di TypeScript. ### Funzioni di asserzione In TypeScript, le funzioni di asserzione sono funzioni che indicano la verifica di una condizione specifica in base al loro valore di ritorno. Nella loro forma più semplice, una funzione di asserzione esamina un predicato fornito e genera un errore quando il predicato restituisce false. ```typescript function isNumber(value: unknown): asserts value is number { if (typeof value !== 'number') { throw new Error('Not a number'); } } ``` Oppure può essere dichiarato come espressione di funzione: ```typescript type AssertIsNumber = (value: unknown) => asserts value is number; const isNumber: AssertIsNumber = value => { if (typeof value !== 'number') { throw new Error('Not a number'); } }; ``` Le funzioni di asserzione condividono alcune somiglianze con le type guard. Le type guard sono state inizialmente introdotte per eseguire controlli in fase di esecuzione e garantire il tipo di un valore all'interno di un ambito specifico. Nello specifico, una type guard è una funzione che valuta un predicato di tipo e restituisce un valore booleano che indica se il predicato è vero o falso. Questo differisce leggermente dalle funzioni di asserzione, in cui l'intenzione è quella di generare un errore anziché restituire false quando il predicato non è soddisfatto. Esempio di type guard: ```typescript const isNumber = (value: unknown): value is number => typeof value === 'number'; ``` ### Tipi di tupla variadici I tipi di tupla variadici sono una funzionalità introdotta nella versione 4.0 di TypeScript. Iniziamo a conoscerli ripassando cos'è una tupla: Un tipo di tupla è un array di lunghezza definita, di cui è noto il tipo di ogni elemento: ```typescript type Student = [string, number]; const [name, age]: Student = ['Simone', 20]; ``` Il termine "variadico" significa indefinito (accetta un numero variabile di argomenti). Una tupla variadica è un tipo di tupla che ha tutte le proprietà di prima, ma la forma esatta non è ancora definita: ```typescript type Bar = [boolean, ...T, number]; type A = Bar<[boolean]>; // [booleano, booleano, numero] type B = Bar<['a', 'b']>; // [boolean, 'a', 'b', number] type C = Bar<[]>; // [boolean, number] ``` Nel codice precedente possiamo vedere che la forma della tupla è definita dal generico `T` passato. Le tuple variadiche possono accettare più generici, il che le rende molto flessibili: ```typescript type Bar = [...T, boolean, ...G]; type A = Bar<[number], [string]>; // [number, boolean, string] type B = Bar<['a', 'b'], [boolean]>; // ["a", "b", boolean, boolean] ``` Con le nuove tuple variadiche possiamo usare: * Gli spread nella sintassi dei tipi di tupla ora possono essere generici, quindi possiamo rappresentare operazioni di ordine superiore su tuple e array anche quando non conosciamo i tipi effettivi su cui stiamo operando. * Gli elementi rimanenti possono trovarsi ovunque in una tupla. Esempio: ```typescript type Items = readonly unknown[]; function concat( arr1: T, arr2: U ): [...T, ...U] { return [...arr1, ...arr2]; } concat([1, 2, 3], ['4', '5', '6']); // [1, 2, 3, "4", "5", "6"] ``` ### Tipi boxed I tipi boxed si riferiscono agli oggetti wrapper utilizzati per rappresentare i tipi primitivi come oggetti. Questi oggetti wrapper forniscono funzionalità e metodi aggiuntivi che non sono disponibili direttamente sui valori primitivi. Quando si accede a un metodo come `charAt` o `normalize` su una primitiva `string`, JavaScript lo racchiude in un oggetto `String`, chiama il metodo e quindi elimina l'oggetto. Dimostrazione: ```typescript const originalNormalize = String.prototype.normalize; String.prototype.normalize = function () { console.log(this, typeof this); return originalNormalize.call(this); }; console.log('\u0041'.normalize()); ``` TypeScript rappresenta questa differenziazione fornendo tipi separati per le primitive e i corrispondenti wrapper di oggetti: * string => String * number => Number * boolean => Boolean * symbol => Symbol * bigint => BigInt I tipi boxed di solito non sono necessari. Evitare di utilizzare tipi boxed e utilizzare invece type per le primitive, ad esempio `string` invece di `String`. ### Covarianza e Controvarianza in TypeScript Covarianza e Controvarianza vengono utilizzate per descrivere il funzionamento delle relazioni quando si ha a che fare con l'ereditarietà o l'assegnazione di tipi. Covarianza significa che una relazione di tipo preserva la direzione dell'ereditarietà o dell'assegnazione, quindi se un tipo A è un sottotipo del tipo B, anche un array di tipo A è considerato un sottotipo di un array di tipo B. La cosa importante da notare qui è che la relazione di sottotipo viene mantenuta, il che significa che Covarianza accetta il sottotipo ma non il supertipo. La controvarianza significa che una relazione di tipo inverte la direzione dell'ereditarietà o dell'assegnazione, quindi se un tipo A è un sottotipo del tipo B, allora un array di tipo B è considerato un sottotipo di un array di tipo A. La relazione di sottotipo è invertita, il che significa che la controvarianza accetta il supertipo ma non il sottotipo. Note: La bivarianza significa accettare sia il supertipo che il sottotipo. Esempio: supponiamo di avere uno spazio per tutti gli animali e uno spazio separato solo per i cani. In covarianza, puoi inserire tutti i cani nello spazio degli animali perché i cani sono un tipo di animale. Ma non puoi inserire tutti gli animali nello spazio dei cani perché potrebbero esserci altri animali mescolati. In controvarianza, non puoi inserire tutti gli animali nello spazio dei cani perché lo spazio degli animali potrebbe contenere anche altri animali. Tuttavia, puoi inserire tutti i cani nello spazio degli animali perché tutti i cani sono anche animali. ```typescript // Esempio di covarianza class Animal { name: string; constructor(name: string) { this.name = name; } } class Dog extends Animal { breed: string; constructor(name: string, breed: string) { super(name); this.breed = breed; } } let animals: Animal[] = []; let dogs: Dog[] = []; // La covarianza consente di assegnare l'array del sottotipo (Dog) all'array del supertipo (Animal) animals = dogs; dogs = animals; // Non valido: il tipo 'Animal[]' non è assegnabile al tipo 'Dog[]' // Esempio di controvarianza type Feed = (animal: T) => void; let feedAnimal: Feed = (animal: Animal) => { console.log(`Nome animale: ${animal.name}`); }; let feedDog: Feed = (dog: Dog) => { console.log(`Nome del cane: ${dog.name}, Razza: ${dog.breed}`); }; // La controvarianza consente di assegnare la callback del supertipo (Animal) alla callback del sottotipo (Dog) feedDog = feedAnimal; feedAnimal = feedDog; // Non valido: il tipo 'Feed' non è assegnabile al tipo 'Feed'. ``` In TypeScript, le relazioni di tipo per gli array sono covarianti, mentre le relazioni di tipo per i parametri di funzione sono controvarianti. Ciò significa che TypeScript presenta sia covarianza che controvarianza, a seconda del contesto. #### Annotazioni di varianza opzionali per i parametri di tipo A partire da TypeScript 4.7.0, possiamo usare le parole chiave `out` e `in` per specificare l'annotazione di varianza. Per la covarianza, usare la parola chiave `out`: ```typescript type AnimalCallback = () => T; // T è covariante in questo caso ``` E per la controvarianza, usare la parola chiave `in`: ```typescript type AnimalCallback = (value: T) => void; // T è controvariante in questo caso ``` ### Firme di indice con pattern di stringhe modello Le firme di indice con pattern di stringhe modello ci consentono di definire firme di indice flessibili utilizzando pattern di stringhe modello. Questa funzionalità ci consente di creare oggetti che possono essere indicizzati con pattern specifici di chiavi stringa, offrendo maggiore controllo e specificità durante l'accesso e la manipolazione delle proprietà. TypeScript dalla versione 4.4 consente firme di indice per simboli e pattern di stringhe modello. ```typescript const uniqueSymbol = Symbol('description'); type MyKeys = `key-${string}`; type MyObject = { [uniqueSymbol]: string; [key: MyKeys]: number; }; const obj: MyObject = { [uniqueSymbol]: 'Chiave simbolo univoca', 'key-a': 123, 'key-b': 456, }; console.log(obj[uniqueSymbol]); // Chiave simbolo univoca console.log(obj['key-a']); // 123 console.log(obj['key-b']); // 456 ``` ### Operatore `satisfies` L'operatore `satisfies` consente di verificare se un dato tipo soddisfa una specifica interfaccia o condizione. In altre parole, garantisce che un tipo abbia tutte le proprietà e i metodi richiesti da una specifica interfaccia. È un modo per garantire che una variabile rientri nella definizione di un tipo. Ecco un esempio: ```typescript type Columns = 'name' | 'nickName' | 'attributes'; type User = Record; // Annotazione del tipo tramite `User` const user: User = { name: 'Simone', nickName: undefined, attributes: ['dev', 'admin'], }; // Nelle righe seguenti, TypeScript non sarà in grado di dedurre correttamente user.attributes?.map(console.log); // La proprietà 'map' non esiste sul tipo 'string | string[]'. La proprietà 'map' non esiste sul tipo 'string'. user.nickName; // string | string[] | undefined // Asserzione di tipo tramite `as` const user2 = { name: 'Simon', nickName: undefined, attributes: ['dev', 'admin'], } as User; // Anche in questo caso, TypeScript non sarà in grado di dedurre correttamente user2.attributes?.map(console.log); // La proprietà 'map' non esiste sul tipo 'string | string[]'. La proprietà 'map' non esiste sul tipo 'string'. user2.nickName; // string | string[] | undefined // Utilizzando gli operatori `satisfies` ora possiamo dedurre correttamente i tipi const user3 = { name: 'Simon', nickName: undefined, attributes: ['dev', 'admin'], } satisfies User; user3.attributes?.map(console.log); // TypeScript deduce correttamente: string[] user3.nickName; // TypeScript deduce correttamente: undefined ``` ### Importazioni ed esportazioni solo per tipo Le importazioni ed esportazioni solo per tipo consentono di importare o esportare tipi senza importare o esportare i valori o le funzioni associati a tali tipi. Questo può essere utile per ridurre le dimensioni del bundle. Per utilizzare le importazioni solo per tipo, è possibile utilizzare la parola chiave `import type`. TypeScript consente l'utilizzo di estensioni di file sia di dichiarazione che di implementazione (.ts, .mts, .cts e .tsx) nelle importazioni solo tipo, indipendentemente dalle impostazioni `allowImportingTsExtensions`. Ad esempio: ```typescript import type { House } from './house.ts'; ``` Sono supportati i seguenti formati: ```typescript import type T from './mod'; import type { A, B } from './mod'; import type * as Types from './mod'; export type { T }; export type { T } from './mod'; ``` ### Dichiarazione using e Gestione Risorse Esplicita Una dichiarazione `using` è un binding immutabile con ambito a blocco, simile a `const`, utilizzato per la gestione delle risorse usa e getta. Quando inizializzato con un valore, il metodo `Symbol.dispose` di quel valore viene registrato e successivamente eseguito all'uscita dall'ambito del blocco che lo racchiude. Questo si basa sulla funzionalità di Gestione Risorse di ECMAScript, utile per eseguire attività di pulizia essenziali dopo la creazione di oggetti, come la chiusura di connessioni, l'eliminazione di file e il rilascio di memoria. Note: * A causa della sua recente introduzione nella versione 5.2 di TypeScript, la maggior parte dei runtime non dispone di supporto nativo. Sono necessari polyfill per: `Symbol.dispose`, `Symbol.asyncDispose`, `DisposableStack`, `AsyncDisposableStack`, `SuppressedError`. \* Inoltre, dovrai configurare il tuo file tsconfig.json come segue: ```json { "compilerOptions": { "target": "es2022", "lib": ["es2022", "esnext.disposable", "dom"] } } ``` Esempio: ```typescript //@ts-ignore Symbol.dispose ??= Symbol('Symbol.dispose'); // Simple polyfill const doWork = (): Disposable => { return { [Symbol.dispose]: () => { console.log('disposed'); }, }; }; console.log(1); { using work = doWork(); // La risorsa è dichiarata console.log(2); } // La risorsa viene eliminata (ad esempio, viene valutata `work[Symbol.dispose]()`) console.log(3); ``` Il codice registrerà: ```shell 1 2 disposed 3 ``` Una risorsa idonea per l'eliminazione deve rispettare l'interfaccia `Disposable`: ```typescript // lib.esnext.disposable.d.ts interface Disposable { [Symbol.dispose](): void; } ``` Le dichiarazioni `using` registrano le operazioni di eliminazione delle risorse in uno stack, assicurandosi che vengano eliminate nell'ordine inverso rispetto alla dichiarazione: ```typescript { using j = getA(), y = getB(); using k = getC(); } // elimina `C`, poi `B`, poi `A`. ``` È garantito che le risorse vengano eliminate, anche se si verificano codice o eccezioni successive. Questo potrebbe portare alla generazione di un'eccezione durante l'eliminazione, con la possibile soppressione di un'altra. Per conservare le informazioni sugli errori soppressi, è stata introdotta una nuova eccezione nativa, `SuppressedError`. #### dichiarazione await using Una dichiarazione `await using` gestisce una risorsa eliminabile in modo asincrono. Il valore deve avere un metodo `Symbol.asyncDispose`, che verrà atteso alla fine del blocco. ```typescript async function doWorkAsync() { await using work = doWorkAsync(); // La risorsa viene dichiarata } // La risorsa viene eliminata (ad esempio, viene valutata `await work[Symbol.asyncDispose]()`) ``` Per una risorsa eliminabile in modo asincrono, deve aderire all'interfaccia `Disposable` o `AsyncDisposable`: ```typescript // lib.esnext.disposable.d.ts interface AsyncDisposable { [Symbol.asyncDispose](): Promise; } ``` ```typescript //@ts-ignore Symbol.asyncDispose ??= Symbol('Symbol.asyncDispose'); // Simple polyfill class DatabaseConnection implements AsyncDisposable { // Un metodo che viene chiamato quando l'oggetto viene eliminato in modo asincrono [Symbol.asyncDispose]() { // Chiude la connessione e restituisce una promessa return this.close(); } async close() { console.log('Chiusura della connessione...'); await new Promise(resolve => setTimeout(resolve, 1000)); console.log('Connessione chiusa.'); } } async function doWork() { // Crea una nuova connessione e la elimina in modo asincrono quando esce dall'ambito await using connection = new DatabaseConnection(); // La risorsa viene dichiarata console.log('Sto lavorando...'); } // La risorsa viene eliminata (ad esempio, viene valutato `await connection[Symbol.asyncDispose]()`) doWork(); ``` Il codice registra: ```shell Sto lavorando... Chiusura della connessione... Connessione chiusa. ``` Le dichiarazioni `using` e `await using` sono consentite nelle istruzioni: `for`, `for-in`, `for-of`, `for-await-of`, `switch`. ### Attributi di importazione Gli attributi di importazione di TypeScript 5.3 (etichette per le importazioni) indicano al runtime come gestire i moduli (JSON, ecc.). Questo migliora la sicurezza garantendo importazioni chiare e si allinea con la Content Security Policy (CSP) per un caricamento più sicuro delle risorse. TypeScript garantisce che siano validi, ma lascia che sia il runtime a gestirne l'interpretazione per la gestione di moduli specifici. Esempio: ```typescript import config from './config.json' with { type: 'json' }; ``` con importazione dinamica: ```typescript const config = import('./config.json', { with: { type: 'json' } }); ``` ================================================ FILE: README-pt_BR.md ================================================ # O Livro Conciso de TypeScript O Livro Conciso de TypeScript fornece uma visão geral abrangente e sucinta dos recursos do TypeScript. Ele oferece explicações claras que abrangem todos os aspectos encontrados na versão mais recente da linguagem, desde o seu poderoso sistema de tipos até recursos avançados. Seja você um iniciante ou um desenvolvedor experiente, este livro é um recurso inestimável para aprimorar sua compreensão e proficiência em TypeScript. Este livro é completamente Gratuito e de Código Aberto (Open Source). Acredito que a educação técnica de alta qualidade deve ser acessível a todos, por isso mantenho este livro gratuito e aberto. Se o livro te ajudou a resolver um bug, entender um conceito difícil ou avançar em sua carreira, considere apoiar meu trabalho pagando quanto quiser (preço sugerido: 15 USD) ou patrocinando um café. Seu apoio me ajuda a manter o conteúdo atualizado e a expandi-lo com novos exemplos e explicações mais profundas. [![Buy Me a Coffee](https://img.shields.io/badge/buy_me_a_coffee-FFDD00?style=for-the-badge&logo=buy-me-a-coffee&logoColor=black)](https://www.buymeacoffee.com/simonepoggiali) [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/donate/?business=QW82ZS956XLFY&no_recurring=0¤cy_code=EUR) ## Traduções Este livro foi traduzido para várias versões de idiomas, incluindo: [Chinês](https://github.com/gibbok/typescript-book/blob/main/README-zh_CN.md) [Italiano](https://github.com/gibbok/typescript-book/blob/main/README-it_IT.md) [Português (Brasil)](https://github.com/gibbok/typescript-book/blob/main/README-pt_BR.md) [Sueco](https://github.com/gibbok/typescript-book/blob/main/README-sv_SE.md) ## Downloads e site Você também pode baixar a versão Epub: [https://github.com/gibbok/typescript-book/tree/main/downloads](https://github.com/gibbok/typescript-book/tree/main/downloads) Uma versão online está disponível em: [https://gibbok.github.io/typescript-book](https://gibbok.github.io/typescript-book) ## Sumário - [O Livro Conciso de TypeScript](#o-livro-conciso-de-typescript) - [Traduções](#traduções) - [Downloads e site](#downloads-e-site) - [Sumário](#sumário) - [Introdução](#introdução) - [Sobre o Autor](#sobre-o-autor) - [Introdução ao TypeScript](#introdução-ao-typescript) - [O que é TypeScript?](#o-que-é-typescript) - [Por que TypeScript?](#por-que-typescript) - [TypeScript e JavaScript](#typescript-e-javascript) - [Geração de Código TypeScript](#geração-de-código-typescript) - [JavaScript Moderno Agora (Downleveling)](#javascript-moderno-agora-downleveling) - [Começando com TypeScript](#começando-com-typescript) - [Instalação](#instalação) - [Configuração](#configuração) - [Arquivo de Configuração do TypeScript](#arquivo-de-configuração-do-typescript) - [target](#target) - [lib](#lib) - [strict](#strict) - [module](#module) - [moduleResolution](#moduleresolution) - [esModuleInterop](#esmoduleinterop) - [jsx](#jsx) - [skipLibCheck](#skiplibcheck) - [files](#files) - [include](#include) - [exclude](#exclude) - [importHelpers](#importhelpers) - [Conselhos para Migração para TypeScript](#conselhos-para-migração-para-typescript) - [Explorando o Sistema de Tipos](#explorando-o-sistema-de-tipos) - [O Serviço de Linguagem do TypeScript](#o-serviço-de-linguagem-do-typescript) - [Tipagem Estrutural](#tipagem-estrutural) - [Regras Fundamentais de Comparação do TypeScript](#regras-fundamentais-de-comparação-do-typescript) - [Tipos como Conjuntos](#tipos-como-conjuntos) - [Atribuir um tipo: Declarações de Tipo e Asserções de Tipo](#atribuir-um-tipo-declarações-de-tipo-e-asserções-de-tipo) - [Declaração de Tipo](#declaração-de-tipo) - [Asserção de Tipo](#asserção-de-tipo) - [Declarações de Ambiente (Ambient Declarations)](#declarações-de-ambiente-ambient-declarations) - [Verificação de Propriedades e Verificação de Excesso de Propriedades](#verificação-de-propriedades-e-verificação-de-excesso-de-propriedades) - [Tipos Fracos (Weak Types)](#tipos-fracos-weak-types) - [Verificação Estrita de Objeto Literal (Freshness)](#verificação-estrita-de-objeto-literal-freshness) - [Inferência de Tipo](#inferência-de-tipo) - [Inferências Mais Avançadas](#inferências-mais-avançadas) - [Alargamento de Tipo (Type Widening)](#alargamento-de-tipo-type-widening) - [Const](#const) - [Modificador Const em Parâmetros de Tipo](#modificador-const-em-parâmetros-de-tipo) - [Asserção Const](#asserção-const-const-assertion) - [Anotação de Tipo Explícita](#anotação-de-tipo-explícita) - [Estreitamento de Tipo (Type Narrowing)](#estreitamento-de-tipo-type-narrowing) - [Condições](#condições) - [Lançando ou retornando](#lançando-ou-retornando) - [União Discriminada](#união-discriminada) - [Proteções de Tipo Definidas pelo Usuário (User-Defined Type Guards)](#proteções-de-tipo-definidas-pelo-usuário-user-defined-type-guards) - [Tipos Primitivos](#tipos-primitivos) - [string](#string) - [boolean](#boolean) - [number](#number) - [bigInt](#bigint) - [Symbol](#symbol) - [null e undefined](#null-e-undefined) - [Array](#array) - [any](#any) - [Anotações de Tipo](#anotações-de-tipo) - [Propriedades Opcionais](#propriedades-opcionais) - [Propriedades Somente Leitura (Readonly)](#propriedades-somente-leitura-readonly) - [Assinaturas de Índice (Index Signatures)](#assinaturas-de-índice-index-signatures) - [Estendendo Tipos](#estendendo-tipos) - [Tipos Literais](#tipos-literais) - [Inferência Literal](#inferência-literal) - [strictNullChecks](#strictnullchecks) - [Enums](#enums) - [Enums numéricos](#enums-numéricos) - [Enums de string](#enums-de-string) - [Enums constantes](#enums-constantes) - [Mapeamento reverso](#mapeamento-reverso) - [Enums de ambiente](#enums-de-ambiente) - [Membros computados e constantes](#membros-computados-e-constantes) - [Estreitamento (Narrowing)](#estreitamento-narrowing) - [typeof type guards](#typeof-type-guards) - [Estreitamento de veracidade (Truthiness narrowing)](#estreitamento-de-veracidade-truthiness-narrowing) - [Estreitamento de igualdade (Equality narrowing)](#estreitamento-de-igualdade-equality-narrowing) - [Estreitamento com operador In](#estreitamento-com-operador-in) - [Estreitamento com instanceof](#estreitamento-com-instanceof) - [Atribuições](#atribuições) - [Análise de Fluxo de Controle](#análise-de-fluxo-de-controle) - [Predicados de Tipo](#predicados-de-tipo) - [Uniões Discriminadas](#uniões-discriminadas) - [O tipo never](#o-tipo-never) - [Verificação de exaustividade](#verificação-de-exaustividade) - [Tipos de Objeto](#tipos-de-objeto) - [Tipo Tupla (Anônimo)](#tipo-tupla-anônimo) - [Tipo Tupla Nomeado (Rotulado)](#tipo-tupla-nomeado-rotulado) - [Tupla de Comprimento Fixo](#tupla-de-comprimento-fixo) - [Tipo União](#tipo-união) - [Tipos de Interseção](#tipos-de-interseção) - [Indexação de Tipo](#indexação-de-tipo) - [Tipo a partir de Valor](#tipo-a-partir-de-valor) - [Tipo a partir de Retorno de Função](#tipo-a-partir-de-retorno-de-função) - [Tipo a partir de Módulo](#tipo-a-partir-de-módulo) - [Tipos Mapeados](#tipos-mapeados) - [Modificadores de Tipos Mapeados](#modificadores-de-tipos-mapeados) - [Tipos Condicionais](#tipos-condicionais-conditional-types) - [Tipos Condicionais Distributivos](#tipos-condicionais-distributivos) - [Inferência de tipo infer em Tipos Condicionais](#infer-inferência-de-tipo-em-tipos-condicionais) - [Tipos Condicionais Predefinidos](#tipos-condicionais-predefinidos) - [Tipos de União de Template](#tipos-de-união-de-template-template-union-types) - [Tipo Any](#tipo-any) - [Tipo Unknown](#tipo-unknown) - [Tipo Void](#tipo-void) - [Tipo Never](#tipo-never) - [Interface e Tipo](#interface-e-type) - [Sintaxe Comum](#sintaxe-comum) - [Tipos Básicos](#tipos-básicos) - [Objetos e Interfaces](#objetos-e-interfaces) - [Tipos União e Interseção](#tipos-união-e-interseção) - [Primitivos de Tipo Integrados](#tipos-primitivos-integrados) - [Objetos JS Integrados Comuns](#objetos-js-integrados-comuns) - [Sobrecargas](#sobrecargas-overloads) - [Mesclagem e Extensão](#mesclagem-e-extensão) - [Diferenças entre Type e Interface](#diferenças-entre-type-e-interface) - [Classe](#classes) - [Sintaxe Comum de Classe](#sintaxe-comum-de-classes) - [Construtor](#construtor) - [Construtores Privados e Protegidos](#construtores-privados-e-protegidos) - [Modificadores de Acesso](#modificadores-de-acesso) - [Get e Set](#get-e-set) - [Auto-Accessors em Classes](#auto-acessores-em-classes) - [this](#this) - [Propriedades de Parâmetro](#propriedades-de-parâmetro) - [Classes Abstratas](#classes-abstratas) - [Com Genéricos](#com-genéricos) - [Decoradores](#decoradores-decorators) - [Decoradores de Classe](#decoradores-de-classe-class-decorators) - [Decorador de Propriedade](#decorador- de-propriedade-property-decorator) - [Decorador de Método](#decorador-de-método-method-decorator) - [Decoradores de Getter e Setter](#decoradores-de-getter-e-setter) - [Metadados de Decorador](#metadados-de-decorador-decorator-metadata) - [Herança](#herança) - [Estáticos](#estáticos-statics) - [Inicialização de propriedade](#inicialização-de-propriedade) - [Sobrecarga de método](#sobrecarga-de-método) - [Genéricos](#genéricos-generics) - [Tipo Genérico](#tipo-genérico) - [Classes Genéricas](#classes-genéricas) - [Restrições Genéricas](#restrições-genéricas-generic-constraints) - [Estreitamento contextual genérico](#estreitamento-contextual-genérico) - [Tipos Estruturais Apagados (Erased Structural Types)](#tipos-estruturais-apagados) - [Namespacing](#namespacing) - [Símbolos](#símbolos-symbols) - [Diretivas de Barra Tripla](#diretivas-triple-slash) - [Manipulação de Tipos](#manipulação-de-tipos) - [Criando Tipos a partir de Tipos](#criando-tipos-a-partir-de-tipos) - [Tipos de Acesso Indexado](#tipos-de-acesso-indexado-indexed-access-types) - [Tipos Utilitários](#tipos-utilitários-utility-types) - [Awaited\](#awaitedt) - [Partial\](#partialt) - [Required\](#requiredt) - [Readonly\](#readonlyt) - [Record\](#recordk-t) - [Pick\](#pickt-k) - [Omit\](#omitt-k) - [Exclude\](#excludet-u) - [Extract\](#extractt-u) - [NonNullable\](#nonnullablet) - [Parameters\](#parameterst) - [ConstructorParameters\](#constructorparameterst) - [ReturnType\](#returntypet) - [InstanceType\](#instancetypet) - [ThisParameterType\](#thisparametertypet) - [OmitThisParameter\](#omitthisparametert) - [ThisType\](#thistypet) - [Uppercase\](#uppercaset) - [Lowercase\](#lowercaset) - [Capitalize\](#capitalizet) - [Uncapitalize\](#uncapitalizet) - [NoInfer\](#noinfert) - [Outros](#outros) - [Tratamento de Erros e Exceções](#erros-e-tratamento-de-exceções) - [Classes Mixin](#classes-mixin-mixin-classes) - [Recursos de Linguagem Assíncronos](#recursos-de-linguagem-assíncronos) - [Iteradores e Geradores](#iteradores-e-geradores) - [Referência JSDoc TsDocs](#referência-jsdoc-tsdocs) - [@types](#types) - [JSX](#jsx-1) - [Módulos ES6](#módulos-es6) - [Operador de Exponenciação ES7](#operador-de-exponenciação-es7) - [A instrução for-await-of](#a-instrução-for-await-of) - [Nova meta-propriedade target](#nova-meta-propriedade-target) - [Expressões de Importação Dinâmica](#expressões-de-importação-dinâmica) - ["tsc –watch"](#tsc-watch) - [Operador de Asserção Não-nula](#operador-de-asserção-não-nulo-non-null-assertion-operator) - [Declarações padronizadas](#declarações-com-valor-padrão-defaulted-declarations) - [Encadeamento Opcional (Optional Chaining)](#encadeamento-opcional-optional-chaining) - [Operador de coalescência nula](#operador-de-coalescência-nula-nullish-coalescing-operator) - [Tipos de Literais de Template](#tipos-de-literal-de-template-template-literal-types) - [Sobrecarga de função](#sobrecarga-de-função-function-overloading) - [Tipos Recursivos](#tipos-recursivos) - [Tipos Condicionais Recursivos](#tipos-condicionais-recursivos) - [Suporte a Módulo ECMAScript no Node](#suporte-a-módulos-ecmascript-no-node) - [Funções de Asserção](#funções-de-asserção-assertion-functions) - [Tipos de Tupla Variádicos](#tipos-de-tupla-variádicos-variadic-tuple-types) - [Tipos Boxed](#tipos-boxed-boxed-types) - [Covariância e Contravariância no TypeScript](#covariância-e-contravariância-no-typescript) - [Anotações de Variância Opcionais para Parâmetros de Tipo](#anotações-de-variância-opcionais-para-parâmetros-de-tipo) - [Assinaturas de Índice de Padrão de String de Template](#assinaturas-de-índice-de-padrão-de-string-de-template-template-string-pattern-index-signatures) - [O Operador satisfies](#o-operador-satisfies) - [Importações e Exportações Apenas de Tipo](#importações-e-exportações-apenas-de-tipo-type-only-imports-and-export) - [Declaração using e Gerenciamento Explícito de Recursos](#declaração-using-e-gerenciamento-explícito-de-recursos-explicit-resource-management) - [Declaração await using](#declaração-await-using) - [Atributos de Importação](#atributos-de-importação-import-attributes) ## Introdução Bem-vindo ao Livro Conciso de TypeScript! Este guia o equipa com conhecimentos essenciais e habilidades práticas para o desenvolvimento eficaz em TypeScript. Descubra conceitos e técnicas fundamentais para escrever código limpo e robusto. Seja você um iniciante ou um desenvolvedor experiente, este livro serve tanto como um guia abrangente quanto como uma referência prática para aproveitar o poder do TypeScript em seus projetos. Este livro cobre o TypeScript 5.2. ## Sobre o Autor Simone Poggiali é um Engenheiro Staff experiente com paixão por escrever código de nível profissional desde os anos 90. Ao longo de sua carreira internacional, contribuiu para inúmeros projetos para uma ampla gama de clientes, de startups a grandes organizações. Empresas notáveis como HelloFresh, Siemens, O2, Leroy Merlin e Snowplow se beneficiaram de sua expertise e dedicação. Você pode encontrar Simone Poggiali nas seguintes plataformas: * LinkedIn: [https://www.linkedin.com/in/simone-poggiali](https://www.linkedin.com/in/simone-poggiali) * GitHub: [https://github.com/gibbok](https://github.com/gibbok) * X.com: [https://x.com/gibbok_coding](https://x.com/gibbok_coding) * Email: gibbok.coding📧gmail.com Lista completa de colaboradores: [https://github.com/gibbok/typescript-book/graphs/contributors](https://github.com/gibbok/typescript-book/graphs/contributors) ## Introdução ao TypeScript ### O que é TypeScript? TypeScript é uma linguagem de programação fortemente tipada que se baseia no JavaScript. Foi originalmente projetada por Anders Hejlsberg em 2012 e é atualmente desenvolvida e mantida pela Microsoft como um projeto de código aberto. O TypeScript compila para JavaScript e pode ser executado em qualquer ambiente de execução JavaScript (por exemplo, um navegador ou Node.js em um servidor). Ele suporta múltiplos paradigmas de programação, como funcional, genérica, imperativa e orientada a objetos, e é uma linguagem compilada (transpilada) que é convertida em JavaScript antes da execução. ### Por que TypeScript? TypeScript é uma linguagem fortemente tipada que ajuda a prevenir erros comuns de programação e a evitar certos tipos de erros em tempo de execução antes que o programa seja executado. Uma linguagem fortemente tipada permite ao desenvolvedor especificar várias restrições e comportamentos do programa nas definições de tipos de dados, facilitando a capacidade de verificar a correção do software e prevenir defeitos. Isso é especialmente valioso em aplicações de larga escala. Alguns dos benefícios do TypeScript: * Tipagem estática, opcionalmente fortemente tipada * Inferência de Tipo * Acesso a recursos ES6 e ES7 * Compatibilidade multiplataforma e entre navegadores * Suporte de ferramentas com IntelliSense ### TypeScript e JavaScript Arquivos TypeScript são escritos em arquivos `.ts` ou `.tsx`, enquanto arquivos JavaScript são escritos em `.js` ou `.jsx`. Arquivos com a extensão `.tsx` ou `.jsx` podem conter a Extensão de Sintaxe JavaScript JSX, que é usada no React para desenvolvimento de UI. O TypeScript é um superconjunto tipado de JavaScript (ECMAScript 2015) em termos de sintaxe. Todo código JavaScript é código TypeScript válido, mas o inverso nem sempre é verdadeiro. Por exemplo, considere uma função em um arquivo JavaScript com a extensão `.js`, como a seguinte: ```typescript const sum = (a, b) => a + b; ``` A função pode ser convertida e usada no TypeScript alterando a extensão do arquivo para `.ts`. No entanto, se a mesma função for anotada com tipos TypeScript, ela não poderá ser executada em nenhum ambiente de execução JavaScript sem compilação. O seguinte código TypeScript produzirá um erro de sintaxe se não for compilado: ```typescript const sum = (a: number, b: number): number => a + b; ``` O TypeScript foi projetado para detectar possíveis exceções que podem ocorrer em tempo de execução durante o tempo de compilação, fazendo com que o desenvolvedor defina a intenção com anotações de tipo. Além disso, o TypeScript também pode capturar problemas se nenhuma anotação de tipo for fornecida. Por exemplo, o seguinte trecho de código não especifica nenhum tipo TypeScript: ```typescript const items = [{ x: 1 }, { x: 2 }]; const result = items.filter(item => item.y); ``` Neste caso, o TypeScript detecta um erro e informa: ```text Property 'y' does not exist on type '{ x: number; }'. ``` O sistema de tipos do TypeScript é amplamente influenciado pelo comportamento de tempo de execução do JavaScript. Por exemplo, o operador de adição (+), que no JavaScript pode realizar a concatenação de strings ou a adição numérica, é modelado da mesma forma no TypeScript: ```typescript const result = '1' + 1; // Result is of type string ``` A equipe por trás do TypeScript tomou a decisão deliberada de sinalizar o uso incomum do JavaScript como erros. Por exemplo, considere o seguinte código JavaScript válido: ```typescript const result = 1 + true; // In JavaScript, the result is equal 2 ``` No entanto, o TypeScript lança um erro: ```text Operator '+' cannot be applied to types 'number' and 'boolean'. ``` Este erro ocorre porque o TypeScript impõe estritamente a compatibilidade de tipos e, neste caso, identifica uma operação inválida entre um número e um booleano. ### Geração de Código TypeScript O compilador TypeScript tem duas responsabilidades principais: verificar se há erros de tipo e compilar para JavaScript. Esses dois processos são independentes um do outro. Os tipos não afetam a execução do código em um ambiente de execução JavaScript, pois são completamente apagados durante a compilação. O TypeScript ainda pode gerar JavaScript mesmo na presença de erros de tipo. Aqui está um exemplo de código TypeScript com um erro de tipo: ```typescript const add = (a: number, b: number): number => a + b; const result = add('x', 'y'); // Argument of type 'string' is not assignable to parameter of type 'number'. ``` No entanto, ele ainda pode produzir uma saída JavaScript executável: ```typescript 'use strict'; const add = (a, b) => a + b; const result = add('x', 'y'); // xy ``` Não é possível verificar tipos TypeScript em tempo de execução. Por exemplo: ```typescript interface Animal { name: string; } interface Dog extends Animal { bark: () => void; } interface Cat extends Animal { meow: () => void; } const makeNoise = (animal: Animal) => { if (animal instanceof Dog) { // 'Dog' only refers to a type, but is being used as a value here. // ... } }; ``` Como os tipos são apagados após a compilação, não há como executar este código em JavaScript. Para reconhecer tipos em tempo de execução, precisamos usar outro mecanismo. O TypeScript fornece várias opções, sendo uma comum a "união tagueada" (tagged union). Por exemplo: ```typescript interface Dog { kind: 'dog'; // Tagged union bark: () => void; } interface Cat { kind: 'cat'; // Tagged union meow: () => void; } type Animal = Dog | Cat; const makeNoise = (animal: Animal) => { if (animal.kind === 'dog') { animal.bark(); } else { animal.meow(); } }; const dog: Dog = { kind: 'dog', bark: () => console.log('bark'), }; makeNoise(dog); ``` A propriedade "kind" é um valor que pode ser usado em tempo de execução para distinguir entre objetos em JavaScript. Também é possível que um valor em tempo de execução tenha um tipo diferente daquele declarado na declaração de tipo. Por exemplo, se o desenvolvedor interpretou mal um tipo de API e o anotou incorretamente. O TypeScript é um superconjunto do JavaScript, portanto a palavra-chave "class" pode ser usada como um tipo e valor em tempo de execução. ```typescript class Animal { constructor(public name: string) {} } class Dog extends Animal { constructor( public name: string, public bark: () => void ) { super(name); } } class Cat extends Animal { constructor( public name: string, public meow: () => void ) { super(name); } } type Mammal = Dog | Cat; const makeNoise = (mammal: Mammal) => { if (mammal instanceof Dog) { mammal.bark(); } else { mammal.meow(); } }; const dog = new Dog('Fido', () => console.log('bark')); makeNoise(dog); ``` No JavaScript, uma "classe" tem uma propriedade "prototype", e o operador "instanceof" pode ser usado para testar se a propriedade prototype de um construtor aparece em qualquer lugar na cadeia de protótipos de um objeto. O TypeScript não tem efeito no desempenho em tempo de execução, pois todos os tipos serão apagados. No entanto, o TypeScript introduz alguma sobrecarga no tempo de compilação. ### JavaScript Moderno Agora (Downleveling) O TypeScript pode compilar código para qualquer versão lançada do JavaScript desde o ECMAScript 3 (1999). Isso significa que o TypeScript pode transpilar o código dos recursos JavaScript mais recentes para versões mais antigas, um processo conhecido como *Downleveling*. Isso permite o uso do JavaScript moderno, mantendo a compatibilidade máxima com ambientes de execução mais antigos. É importante notar que durante a transpilação para uma versão mais antiga do JavaScript, o TypeScript pode gerar código que pode incorrer em uma sobrecarga de desempenho em comparação com as implementações nativas. Aqui estão alguns dos recursos modernos do JavaScript que podem ser usados no TypeScript: * Módulos ECMAScript em vez de callbacks "define" no estilo AMD ou instruções "require" do CommonJS. * Classes em vez de protótipos. * Declaração de variáveis usando "let" ou "const" em vez de "var". * Loop "for-of" ou ".forEach" em vez do loop "for" tradicional. * Funções de seta (Arrow functions) em vez de expressões de função. * Atribuição via desestruturação (Destructuring assignment). * Nomes de propriedade/método abreviados e nomes de propriedade computados. * Parâmetros de função padrão. Ao aproveitar esses recursos modernos do JavaScript, os desenvolvedores podem escrever códigos mais expressivos e concisos no TypeScript. ## Começando com TypeScript ### Instalação O Visual Studio Code oferece excelente suporte para a linguagem TypeScript, mas não inclui o compilador TypeScript. Para instalar o compilador TypeScript, você pode usar um gerenciador de pacotes como npm ou yarn: ```shell npm install typescript --save-dev ``` ou ```shell yarn add typescript --dev ``` Certifique-se de realizar o commit do arquivo de bloqueio (lockfile) gerado para garantir que cada membro da equipe use a mesma versão do TypeScript. Para executar o compilador TypeScript, você pode usar os seguintes comandos: ```shell npx tsc ``` ou ```shell yarn tsc ``` Recomenda-se instalar o TypeScript por projeto em vez de globalmente, pois fornece um processo de construção mais previsível. No entanto, para ocasiões pontuais, você pode usar o seguinte comando: ```shell npx tsc ``` ou instalá-lo globalmente: ```shell npm install -g typescript ``` Se você estiver usando o Microsoft Visual Studio, pode obter o TypeScript como um pacote no NuGet para seus projetos MSBuild. No Console do Gerenciador de Pacotes NuGet, execute o seguinte comando: ```shell Install-Package Microsoft.TypeScript.MSBuild ``` Durante a instalação do TypeScript, dois executáveis são instalados: "tsc" como o compilador TypeScript e "tsserver" como o servidor autônomo do TypeScript. O servidor autônomo contém o compilador e os serviços de linguagem que podem ser utilizados por editores e IDEs para fornecer completamento inteligente de código. Além disso, existem vários transpiladores compatíveis com TypeScript disponíveis, como Babel (via um plugin) ou swc. Esses transpiladores podem ser usados para converter código TypeScript em outras linguagens ou versões de destino. ### Configuração O TypeScript pode ser configurado usando as opções da CLI do tsc ou utilizando um arquivo de configuração dedicado chamado tsconfig.json localizado na raiz do projeto. Para gerar um arquivo tsconfig.json pré-preenchido com as configurações recomendadas, você pode usar o seguinte comando: ```shell tsc --init ``` Ao executar o comando `tsc` localmente, o TypeScript compilará o código usando a configuração especificada no arquivo tsconfig.json mais próximo. Aqui estão alguns exemplos de comandos da CLI que rodam com as configurações padrão: ```shell tsc main.ts // Compila um arquivo específico (main.ts) para JavaScript tsc src/*.ts // Compila todos os arquivos .ts na pasta 'src' para JavaScript tsc app.ts util.ts --outfile index.js // Compila dois arquivos TypeScript (app.ts e util.ts) em um único arquivo JavaScript (index.js) ``` ### Arquivo de Configuração do TypeScript Um arquivo tsconfig.json é usado para configurar o Compilador TypeScript (tsc). Geralmente, ele é adicionado à raiz do projeto, junto com o arquivo `package.json`. Notas: * tsconfig.json aceita comentários, mesmo estando no formato json. * É aconselhável usar este arquivo de configuração em vez das opções de linha de comando. No link a seguir você encontra a documentação completa e seu esquema: [https://www.typescriptlang.org/tsconfig](https://www.typescriptlang.org/tsconfig) [https://www.typescriptlang.org/tsconfig/](https://www.typescriptlang.org/tsconfig/) A seguir, apresentamos uma lista das configurações comuns e úteis: #### target A propriedade "target" é usada para especificar qual versão do JavaScript ECMAScript seu TypeScript deve emitir/compilar. Para navegadores modernos, o ES6 é uma boa opção; para navegadores mais antigos, o ES5 é recomendado. #### lib A propriedade "lib" é usada para especificar quais arquivos de biblioteca incluir no tempo de compilação. O TypeScript inclui automaticamente APIs para recursos especificados na propriedade "target", mas é possível omitir ou escolher bibliotecas específicas para necessidades particulares. Por exemplo, se você estiver trabalhando em um projeto de servidor, pode excluir a biblioteca "DOM", que é útil apenas em um ambiente de navegador. #### strict A propriedade "strict" habilita garantias mais fortes e aumenta a segurança de tipos. É aconselhável incluir sempre esta propriedade no arquivo tsconfig.json do seu projeto. Habilitar a propriedade "strict" permite que o TypeScript possa: * Emitir código usando "use strict" para cada arquivo de origem. * Considerar "null" e "undefined" no processo de verificação de tipos. * Desabilitar o uso do tipo "any" quando não houver anotações de tipo. * Levantar um erro sobre o uso da expressão "this", que de outra forma implicaria o tipo "any". #### module A propriedade "module" define o sistema de módulo suportado para o programa compilado. Durante o tempo de execução, um carregador de módulo é usado para localizar e executar dependências com base no sistema de módulo especificado. Os carregadores de módulos mais comuns usados no JavaScript são o CommonJS do Node.js para aplicações do lado do servidor e o RequireJS para módulos AMD em aplicações web baseadas em navegador. O TypeScript pode emitir código para vários sistemas de módulos, incluindo UMD, System, ESNext, ES2015/ES6 e ES2020. Nota: O sistema de módulos deve ser escolhido com base no ambiente de destino e no mecanismo de carregamento de módulos disponível nesse ambiente. #### moduleResolution A propriedade "moduleResolution" especifica a estratégia de resolução de módulos. Use "node" para código TypeScript moderno; a estratégia "classic" é usada apenas para versões antigas do TypeScript (antes da 1.6). #### esModuleInterop A propriedade "esModuleInterop" permite a importação padrão de módulos CommonJS que não exportaram usando a propriedade "default"; esta propriedade fornece um shim para garantir a compatibilidade no JavaScript emitido. Após habilitar esta opção, podemos usar `import MyLibrary from "my-library"` em vez de `import * as MyLibrary from "my-library"`. #### jsx A propriedade "jsx" aplica-se apenas a arquivos .tsx usados no ReactJS e controla como as construções JSX são compiladas em JavaScript. Uma opção comum é "preserve", que compilará para um arquivo .jsx mantendo o JSX inalterado para que ele possa ser passado para diferentes ferramentas, como o Babel, para transformações posteriores. #### skipLibCheck A propriedade "skipLibCheck" evitará que o TypeScript verifique os tipos de todos os pacotes de terceiros importados. Esta propriedade reduzirá o tempo de compilação de um projeto. O TypeScript ainda verificará seu código em relação às definições de tipo fornecidas por esses pacotes. #### files A propriedade "files" indica ao compilador uma lista de arquivos que devem sempre ser incluídos no programa. #### include A propriedade "include" indica ao compilador uma lista de arquivos que gostaríamos de incluir. Esta propriedade permite padrões semelhantes a glob, como "\**" para qualquer subdiretório, "*" para qualquer nome de arquivo e "?" para caracteres opcionais. #### exclude A propriedade "exclude" indica ao compilador uma lista de arquivos que não devem ser incluídos na compilação. Isso pode incluir arquivos como "node_modules" ou arquivos de teste. Nota: tsconfig.json permite comentários. ### importHelpers O TypeScript usa código auxiliar ao gerar código para certos recursos avançados ou de JavaScript com "down-leveled". Por padrão, esses auxiliares são duplicados nos arquivos que os utilizam. A opção `importHelpers` importa esses auxiliares do módulo `tslib`, tornando a saída do JavaScript mais eficiente. ### Conselhos para Migração para TypeScript Para projetos grandes, recomenda-se adotar uma transição gradual onde o código TypeScript e JavaScript coexistirão inicialmente. Apenas projetos pequenos podem ser migrados para TypeScript de uma só vez. O primeiro passo desta transição é introduzir o TypeScript no processo da cadeia de construção. Isso pode ser feito usando a opção de compilador "allowJs", que permite que arquivos .ts e .tsx coexistam com arquivos JavaScript existentes. Como o TypeScript voltará para um tipo "any" para uma variável quando não puder inferir o tipo dos arquivos JavaScript, recomenda-se desabilitar "noImplicitAny" em suas opções de compilador no início da migração. O segundo passo é garantir que seus testes JavaScript funcionem junto com os arquivos TypeScript, para que você possa executar testes conforme converte cada módulo. Se estiver usando Jest, considere usar o `ts-jest`, que permite testar projetos TypeScript com Jest. O terceiro passo é incluir declarações de tipo para bibliotecas de terceiros em seu projeto. Essas declarações podem ser encontradas empacotadas ou no DefinitelyTyped. Você pode pesquisar por elas usando [https://www.typescriptlang.org/dt/search](https://www.typescriptlang.org/dt/search) e instalá-las usando: ```shell npm install --save-dev @types/package-name ``` ou ```shell yarn add --dev @types/package-name ``` O quarto passo é migrar módulo por módulo com uma abordagem de baixo para cima, seguindo seu Gráfico de Dependências começando pelas folhas. A ideia é começar convertendo Módulos que não dependem de outros Módulos. Para visualizar os gráficos de dependência, você pode usar a ferramenta "madge". Bons módulos candidatos para essas conversões iniciais são funções utilitárias e código relacionado a APIs ou especificações externas. É possível gerar automaticamente definições de tipo TypeScript a partir de contratos Swagger, GraphQL ou esquemas JSON para serem incluídos em seu projeto. Quando não houver especificações ou esquemas oficiais disponíveis, você pode gerar tipos a partir de dados brutos, como JSON retornado por um servidor. No entanto, recomenda-se gerar tipos a partir de especificações em vez de dados para evitar perder casos extremos. Durante a migração, evite a refatoração de código e concentre-se apenas em adicionar tipos aos seus módulos. O quinto passo é habilitar o "noImplicitAny", que forçará que todos os tipos sejam conhecidos e definidos, proporcionando uma melhor experiência de TypeScript para seu projeto. Durante a migração, você pode usar a diretiva `@ts-check`, que habilita a verificação de tipos do TypeScript em um arquivo JavaScript. Esta diretiva fornece uma versão flexível de verificação de tipos e pode ser usada inicialmente para identificar problemas em arquivos JavaScript. Quando o `@ts-check` é incluído em um arquivo, o TypeScript tentará deduzir definições usando comentários no estilo JSDoc. No entanto, considere usar anotações JSDoc apenas em um estágio muito inicial da migração. Considere manter o valor padrão de `noEmitOnError` no seu tsconfig.json como false. Isso permitirá gerar o código-fonte JavaScript mesmo se erros forem relatados. ## Explorando o Sistema de Tipos ### O Serviço de Linguagem do TypeScript O Serviço de Linguagem do TypeScript, também conhecido como tsserver, oferece vários recursos, como relatório de erros, diagnósticos, compilar ao salvar, renomeação, ir para definição, listas de preenchimento, ajuda de assinatura e muito mais. É usado principalmente por ambientes de desenvolvimento integrados (IDEs) para fornecer suporte ao IntelliSense. Ele se integra perfeitamente ao Visual Studio Code e é utilizado por ferramentas como Conquer of Completion (Coc). Os desenvolvedores podem aproveitar uma API dedicada e criar seus próprios plugins de serviço de linguagem personalizados para aprimorar a experiência de edição do TypeScript. Isso pode ser particularmente útil para implementar recursos especiais de linting ou habilitar o preenchimento automático para uma linguagem de modelagem personalizada. Um exemplo de plugin personalizado do mundo real é o "typescript-styled-plugin", que fornece relatórios de erros de sintaxe e suporte IntelliSense para propriedades CSS em componentes estilizados (styled components). Para mais informações e guias de início rápido, você pode consultar o Wiki oficial do TypeScript no GitHub: [https://github.com/microsoft/TypeScript/wiki/](https://github.com/microsoft/TypeScript/wiki/) ### Tipagem Estrutural O TypeScript é baseado em um sistema de tipos estrutural. Isso significa que a compatibilidade e a equivalência de tipos são determinadas pela estrutura ou definição real do tipo, em vez de seu nome ou local de declaração, como em sistemas de tipos nominativos como C# ou C++. O sistema de tipos estrutural do TypeScript foi projetado com base em como o sistema de tipagem dinâmica "duck typing" do JavaScript funciona durante o tempo de execução. O exemplo a seguir é um código TypeScript válido. Como você pode observar, "X" e "Y" têm o mesmo membro "a", embora tenham nomes de declaração diferentes. Os tipos são determinados por suas estruturas e, neste caso, como as estruturas são as mesmas, eles são compatíveis e válidos. ```typescript type X = { a: string; }; type Y = { a: string; }; const x: X = { a: 'a' }; const y: Y = x; // Válido ``` ### Regras Fundamentais de Comparação do TypeScript O processo de comparação do TypeScript é recursivo e executado em tipos aninhados em qualquer nível. Um tipo "X" é compatível com "Y" se "Y" tiver pelo menos os mesmos membros que "X". ```typescript type X = { a: string; }; const y = { a: 'A', b: 'B' }; // Válido, pois tem pelo menos os mesmos membros que X const r: X = y; ``` Os parâmetros da função são comparados por tipos, não por seus nomes: ```typescript type X = (a: number) => void; type Y = (a: number) => void; let x: X = (j: number) => undefined; let y: Y = (k: number) => undefined; y = x; // Válido x = y; // Válido ``` Os tipos de retorno da função devem ser os mesmos: ```typescript type X = (a: number) => undefined; type Y = (a: number) => number; let x: X = (a: number) => undefined; let y: Y = (a: number) => 1; y = x; // Inválido x = y; // Inválido ``` O tipo de retorno de uma função de origem deve ser um subtipo do tipo de retorno de uma função de destino: ```typescript let x = () => ({ a: 'A' }); let y = () => ({ a: 'A', b: 'B' }); x = y; // Válido y = x; // Inválido, o membro b está faltando ``` Descartar parâmetros de função é permitido, pois é uma prática comum no JavaScript, por exemplo, usando "Array.prototype.map()": ```typescript [1, 2, 3].map((element, _index, _array) => element + 'x'); ``` Portanto, as seguintes declarações de tipo são completamente válidas: ```typescript type X = (a: number) => undefined; type Y = (a: number, b: number) => undefined; let x: X = (a: number) => undefined; let y: Y = (a: number) => undefined; // Falta o parâmetro b y = x; // Válido ``` Quaisquer parâmetros opcionais adicionais do tipo de origem são válidos: ```typescript type X = (a: number, b?: number, c?: number) => undefined; type Y = (a: number) => undefined; let x: X = a => undefined; let y: Y = a => undefined; y = x; // Válido x = y; // Válido ``` Quaisquer parâmetros opcionais do tipo de destino sem parâmetros correspondentes no tipo de origem são válidos e não constituem um erro: ```typescript type X = (a: number) => undefined; type Y = (a: number, b?: number) => undefined; let x: X = a => undefined; let y: Y = a => undefined; y = x; // Válido x = y; // Válido ``` O parâmetro rest é tratado como uma série infinita de parâmetros opcionais: ```typescript type X = (a: number, ...rest: number[]) => undefined; let x: X = a => undefined; // Válido ``` Funções com sobrecargas são válidas se a assinatura da sobrecarga for compatível com sua assinatura de implementação: ```typescript function x(a: string): void; function x(a: string, b: number): void; function x(a: string, b?: number): void { console.log(a, b); } x('a'); // Válido x('a', 1); // Válido function y(a: string): void; // Inválido, não compatível com a assinatura de implementação function y(a: string, b: number): void; function y(a: string, b: number): void { console.log(a, b); } y('a'); y('a', 1); ``` A comparação de parâmetros de função é bem-sucedida se os parâmetros de origem e de destino forem atribuíveis a supertipos ou subtipos (bivariância). ```typescript // Supertipo class X { a: string; constructor(value: string) { this.a = value; } } // Subtipo class Y extends X {} // Subtipo class Z extends X {} type GetA = (x: X) => string; const getA: GetA = x => x.a; // A bivariância aceita supertipos console.log(getA(new X('x'))); // Válido console.log(getA(new Y('Y'))); // Válido console.log(getA(new Z('z'))); // Válido ``` Enums são comparáveis e válidos com números e vice-versa, mas comparar valores de Enum de diferentes tipos de Enum é inválido. ```typescript enum X { A, B, } enum Y { A, B, C, } const xa: number = X.A; // Válido const ya: Y = 0; // Válido X.A === Y.A; // Inválido ``` Instâncias de uma classe estão sujeitas a uma verificação de compatibilidade para seus membros privados e protegidos: ```typescript class X { public a: string; constructor(value: string) { this.a = value; } } class Y { private a: string; constructor(value: string) { this.a = value; } } let x: X = new Y('y'); // Inválido ``` A verificação de comparação não leva em consideração as diferentes hierarquias de herança, por exemplo: ```typescript class X { public a: string; constructor(value: string) { this.a = value; } } class Y extends X { public a: string; constructor(value: string) { super(value); this.a = value; } } class Z { public a: string; constructor(value: string) { this.a = value; } } let x: X = new X('x'); let y: Y = new Y('y'); let z: Z = new Z('z'); x === y; // Válido x === z; // Válido mesmo que z seja de uma hierarquia de herança diferente ``` Genéricos são comparados usando suas estruturas baseadas no tipo resultante após a aplicação do parâmetro genérico; apenas o resultado final é comparado como um tipo não genérico. ```typescript interface X { a: T; } let x: X = { a: 1 }; let y: X = { a: 'a' }; x === y; // Inválido, pois o argumento de tipo é usado na estrutura final ``` ```typescript interface X {} const x: X = 1; const y: X = 'a'; x === y; // Válido, pois o argumento de tipo não é usado na estrutura final ``` Quando os genéricos não têm seu argumento de tipo especificado, todos os argumentos não especificados são tratados como tipos com "any": ```typescript type X = (x: T) => T; type Y = (y: K) => K; let x: X = x => x; let y: Y = y => y; x = y; // Válido ``` Lembre-se: ```typescript let a: number = 1; let b: number = 2; a = b; // Válido, tudo é atribuível a si mesmo let c: any; c = 1; // Válido, todos os tipos são atribuíveis a any let d: unknown; d = 1; // Válido, todos os tipos são atribuíveis a unknown let e: unknown; let e1: unknown = e; // Válido, unknown só é atribuível a si mesmo e a any let e2: any = e; // Válido let e3: number = e; // Inválido let f: never; f = 1; // Inválido, nada é atribuível a never let g: void; let g1: any; g = 1; // Inválido, void não é atribuível a nada, exceto any, nem nada é atribuível a ele g = g1; // Válido ``` Observe que quando "strictNullChecks" está habilitado, "null" e "undefined" são tratados de forma semelhante a "void"; caso contrário, são semelhantes a "never". ### Tipos como Conjuntos No TypeScript, um tipo é um conjunto de valores possíveis. Este conjunto também é conhecido como o domínio do tipo. Cada valor de um tipo pode ser visto como um elemento em um conjunto. Um tipo estabelece as restrições que cada elemento no conjunto deve satisfazer para ser considerado um membro desse conjunto. A principal tarefa do TypeScript é verificar se um conjunto é um subconjunto de outro. O TypeScript suporta vários tipos de conjuntos: | Termo do conjunto | TypeScript | Notas | | ---------------------- | ------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | | Conjunto vazio | never | "never" não contém nada além de si mesmo | | Conjunto de elemento único | undefined / null / tipo literal | | | Conjunto finito | boolean / união | | | Conjunto infinito | string / number / objeto | | | Conjunto universal | any / unknown | Cada elemento é um membro de "any" e cada conjunto é um subconjunto dele / "unknown" é uma contraparte segura em termos de tipo do "any" | Aqui estão alguns exemplos: | TypeScript | Termo do conjunto | Exemplo | | ----------------------- | -------------------------- | ------------------------------------------------------------------------------------- | | never | ∅ (conjunto vazio) | const x: never = 'x'; // Erro: O tipo 'string' não pode ser atribuído ao tipo 'never' | | | | | Tipo literal | Conjunto de elemento único | type X = 'X'; | | | | type Y = 7; | | | | | Valor atribuível a T | Valor ∈ T (membro de) | type XY = 'X' \| 'Y'; | | | | const x: XY = 'X'; | | | | | T1 atribuível a T2 | T1 ⊆ T2 (subconjunto de) | type XY = 'X' \| 'Y'; | | | | const x: XY = 'X'; | | | | const j: XY = 'J'; // O tipo '"J"' não pode ser atribuído ao tipo 'XY'. | | | | | | T1 extends T2 | T1 ⊆ T2 (subconjunto de) | type X = 'X' extends string ? true : false; | | | | | T1 \| T2 | T1 ∪ T2 (união) | type XY = 'X' \| 'Y'; | | | | type JK = 1 \| 2; | | | | | T1 & T2 | T1 ∩ T2 (interseção) | type X = \{ a: string \} | | | | type Y = \{ b: string \} | | | | type XY = X & Y | | | | const x: XY = \{ a: 'a', b: 'b' \} | | | | | unknown | Conjunto universal | const x: unknown = 1 | Uma união, (T1 | T2), cria um conjunto mais amplo (ambos): ```typescript type X = { a: string; }; type Y = { b: string; }; type XY = X | Y; const r: XY = { a: 'a', b: 'x' }; // Válido ``` Uma interseção, (T1 & T2), cria um conjunto mais estreito (apenas o que é compartilhado): ```typescript type X = { a: string; }; type Y = { a: string; b: string; }; type XY = X & Y; const r: XY = { a: 'a' }; // Inválido const j: XY = { a: 'a', b: 'b' }; // Válido ``` A palavra-chave `extends` pode ser considerada como "subconjunto de" neste contexto. Ela define uma restrição para um tipo. O `extends` usado com um genérico trata o genérico como um conjunto infinito e o restringe a um tipo mais específico. Observe que o `extends` nada tem a ver com hierarquia no sentido de Orientação a Objetos (não existe esse conceito no TypeScript). O TypeScript trabalha com conjuntos e não possui uma hierarquia estrita; de fato, como no exemplo abaixo, dois tipos podem se sobrepor sem que nenhum seja um subtipo do outro (o TypeScript considera a estrutura, a forma dos objetos). ```typescript interface X { a: string; } interface Y extends X { b: string; } interface Z extends Y { c: string; } const z: Z = { a: 'a', b: 'b', c: 'c' }; interface X1 { a: string; } interface Y1 { a: string; b: string; } interface Z1 { a: string; b: string; c: string; } const z1: Z1 = { a: 'a', b: 'b', c: 'c' }; const r: Z1 = z; // Válido ``` ### Atribuir um tipo: Declarações de Tipo e Asserções de Tipo Um tipo pode ser atribuído de diferentes maneiras no TypeScript: #### Declaração de Tipo No exemplo a seguir, usamos x: X (": Tipo") para declarar um tipo para a variável x. ```typescript type X = { a: string; }; // Declaração de tipo const x: X = { a: 'a', }; ``` Se a variável não estiver no formato especificado, o TypeScript relatará um erro. Por exemplo: ```typescript type X = { a: string; }; const x: X = { a: 'a', b: 'b', // Erro: O objeto literal só pode especificar propriedades conhecidas }; ``` #### Asserção de Tipo É possível adicionar uma asserção usando a palavra-chave `as`. Isso informa ao compilador que o desenvolvedor tem mais informações sobre um tipo e silencia quaisquer erros que possam ocorrer. Por exemplo: ```typescript type X = { a: string; }; const x = { a: 'a', b: 'b', } as X; ``` No exemplo acima, o objeto x é asseverado como tendo o tipo X usando a palavra-chave `as`. Isso informa ao compilador TypeScript que o objeto está em conformidade com o tipo especificado, embora tenha uma propriedade b adicional não presente na definição do tipo. Asserções de tipo são úteis em situações onde um tipo mais específico precisa ser especificado, especialmente ao trabalhar com o DOM. Por exemplo: ```typescript const myInput = document.getElementById('my_input') as HTMLInputElement; ``` Aqui, a asserção de tipo `as HTMLInputElement` é usada para dizer ao TypeScript que o resultado de `getElementById` deve ser tratado como um `HTMLInputElement`. Asserções de tipo também podem ser usadas para mapear chaves novamente, conforme mostrado no exemplo abaixo com literais de template: ```typescript type J = { [Property in keyof Type as `prefix_${string & Property}`]: () => Type[Property]; }; type X = { a: string; b: number; }; type Y = J; ``` Neste exemplo, o tipo `J` usa um tipo mapeado com um literal de template para mapear as chaves de `Type`. Ele cria novas propriedades com um "prefix_" adicionado a cada chave, e seus valores correspondentes são funções que retornam os valores originais da propriedade. Vale a pena notar que, ao usar uma asserção de tipo, o TypeScript não executará a verificação de excesso de propriedades. Portanto, geralmente é preferível usar uma Declaração de Tipo quando a estrutura do objeto for conhecida antecipadamente. #### Declarações de Ambiente (Ambient Declarations) Declarações de ambiente são arquivos que descrevem tipos para código JavaScript; eles têm o formato de nome de arquivo `.d.ts`. Geralmente são importados e usados para anotar bibliotecas JavaScript existentes ou para adicionar tipos a arquivos JS existentes em seu projeto. Muitos tipos de bibliotecas comuns podem ser encontrados em: [https://github.com/DefinitelyTyped/DefinitelyTyped/](https://github.com/DefinitelyTyped/DefinitelyTyped/) e podem ser instalados usando: ```shell npm install --save-dev @types/nome-da-biblioteca ``` Para suas Declarações de Ambiente definidas, você pode importar usando a referência de "barra tripla": ```typescript /// ``` Você pode usar Declarações de Ambiente até mesmo em arquivos JavaScript usando `// @ts-check`. A palavra-chave `declare` habilita definições de tipo para código JavaScript existente sem importá-lo, servindo como um marcador para tipos de outro arquivo ou globalmente. ### Verificação de Propriedades e Verificação de Excesso de Propriedades O TypeScript é baseado em um sistema de tipos estrutural, mas a verificação de excesso de propriedades é um recurso do TypeScript que permite verificar se um objeto tem exatamente as propriedades especificadas no tipo. A Verificação de Excesso de Propriedades é executada ao atribuir objetos literais a variáveis ou ao passá-los como argumentos para funções, por exemplo. ```typescript type X = { a: string; }; const y = { a: 'a', b: 'b' }; const x: X = y; // Válido por causa da tipagem estrutural const w: X = { a: 'a', b: 'b' }; // Inválido por causa da verificação de excesso de propriedades ``` ### Tipos Fracos (Weak Types) Um tipo é considerado fraco quando não contém nada além de um conjunto de todas as propriedades opcionais: ```typescript type X = { a?: string; b?: string; }; ``` O TypeScript considera um erro atribuir qualquer coisa a um tipo fraco quando não há sobreposição; por exemplo, o seguinte lança um erro: ```typescript type Options = { a?: string; b?: string; }; const fn = (options: Options) => undefined; fn({ c: 'c' }); // Inválido ``` Embora não recomendado, se necessário, é possível ignorar esta verificação usando asserção de tipo: ```typescript type Options = { a?: string; b?: string; }; const fn = (options: Options) => undefined; fn({ c: 'c' } as Options); // Válido ``` Ou adicionando `unknown` à assinatura de índice do tipo fraco: ```typescript type Options = { [prop: string]: unknown; a?: string; b?: string; }; const fn = (options: Options) => undefined; fn({ c: 'c' }); // Válido ``` ### Verificação Estrita de Objeto Literal (Freshness) A verificação estrita de objeto literal, às vezes chamada de "freshness", é um recurso do TypeScript que ajuda a capturar propriedades em excesso ou com erro de ortografia que, de outra forma, passariam despercebidas em verificações normais de tipo estrutural. Ao criar um objeto literal, o compilador TypeScript o considera "fresco" (fresh). Se o objeto literal for atribuído a uma variável ou passado como um parâmetro, o TypeScript lançará um erro se o objeto literal especificar propriedades que não existem no tipo de destino. No entanto, a "freshness" desaparece quando um objeto literal é alargado ou quando uma asserção de tipo é usada. Aqui estão alguns exemplos para ilustrar: ```typescript type X = { a: string }; type Y = { a: string; b: string }; let x: X; x = { a: 'a', b: 'b' }; // Verificação de Freshness: Atribuição inválida var y: Y; y = { a: 'a', bx: 'bx' }; // Verificação de Freshness: Atribuição inválida const fn = (x: X) => console.log(x.a); fn(x); fn(y); // Alargamento (Widening): Sem erros, estruturalmente compatível em termos de tipo fn({ a: 'a', bx: 'b' }); // Verificação de Freshness: Argumento inválido let c: X = { a: 'a' }; let d: Y = { a: 'a', b: '' }; c = d; // Alargamento: Sem verificação de Freshness ``` ### Inferência de Tipo O TypeScript pode inferir tipos quando nenhuma anotação é fornecida durante a: * Inicialização da variável. * Inicialização de membros. * Definição de valores padrão para parâmetros. * Tipo de retorno da função. Por exemplo: ```typescript let x = 'x'; // O tipo inferido é string ``` O compilador TypeScript analisa o valor ou expressão e determina seu tipo com base nas informações disponíveis. ### Inferências Mais Avançadas Quando várias expressões são usadas na inferência de tipo, o TypeScript procura pelos "melhores tipos comuns" (best common types). Por exemplo: ```typescript let x = [1, 'x', 1, null]; // O tipo inferido é: (string | number | null)[] ``` Se o compilador não conseguir encontrar os melhores tipos comuns, ele retorna um tipo de união. Por exemplo: ```typescript let x = [new RegExp('x'), new Date()]; // O tipo inferido é: (RegExp | Date)[] ``` O TypeScript utiliza a "tipagem contextual" baseada na localização da variável para inferir tipos. No exemplo a seguir, o compilador sabe que `e` é do tipo `MouseEvent` por causa do tipo de evento `click` definido no arquivo `lib.d.ts`, que contém declarações de ambiente para várias construções JavaScript comuns e o DOM: ```typescript window.addEventListener('click', function (e) {}); // O tipo inferido de e é MouseEvent ``` ### Alargamento de Tipo (Type Widening) O alargamento de tipo (type widening) é o processo no qual o TypeScript atribui um tipo a uma variável inicializada quando nenhuma anotação de tipo foi fornecida. Ele permite tipos de mais estreitos para mais amplos, mas não o contrário. No exemplo a seguir: ```typescript let x = 'x'; // O TypeScript infere como string, um tipo amplo let y: 'y' | 'x' = 'y'; // o tipo de y é uma união de tipos literais y = x; // Inválido: O tipo 'string' não pode ser atribuído ao tipo '"x" | "y"'. ``` O TypeScript atribui `string` a `x` com base no valor único fornecido durante a inicialização (`x`); este é um exemplo de alargamento. O TypeScript fornece maneiras de ter controle sobre o processo de alargamento, por exemplo, usando "const". ### Const O uso da palavra-chave `const` ao declarar uma variável resulta em uma inferência de tipo mais estreita no TypeScript. Por exemplo: ```typescript const x = 'x'; // O TypeScript infere o tipo de x como 'x', um tipo mais estreito let y: 'y' | 'x' = 'y'; y = x; // Válido: O tipo de x é inferido como 'x' ``` Ao usar `const` para declarar a variável x, seu tipo é estreitado para o valor literal específico 'x'. Como o tipo de x é estreitado, ele pode ser atribuído à variável y sem nenhum erro. A razão pela qual o tipo pode ser inferido é porque as variáveis `const` não podem ser reatribuídas, portanto seu tipo pode ser estreitado para um tipo literal específico, neste caso, o tipo literal 'x'. #### Modificador Const em Parâmetros de Tipo A partir da versão 5.0 do TypeScript, é possível especificar o atributo `const` em um parâmetro de tipo genérico. Isso permite inferir o tipo mais preciso possível. Vejamos um exemplo sem usar `const`: ```typescript function identity(value: T) { // Sem const aqui return value; } const values = identity({ a: 'a', b: 'b' }); // O tipo inferido é: { a: string; b: string; } ``` Como você pode ver, as propriedades `a` e `b` são inferidas com o tipo `string`. Agora, vejamos a diferença com a versão `const`: ```typescript function identity(value: T) { // Usando modificador const em parâmetros de tipo return value; } const values = identity({ a: 'a', b: 'b' }); // O tipo inferido é: { a: "a"; b: "b"; } ``` Agora podemos ver que as propriedades `a` e `b` são inferidas como `const`, portanto `a` e `b` são tratados como literais de string em vez de apenas tipos `string`. #### Asserção Const (Const assertion) Este recurso permite declarar uma variável com um tipo literal mais preciso baseado em seu valor de inicialização, sinalizando ao compilador que o valor deve ser tratado como um literal imutável. Aqui estão alguns exemplos: Em uma única propriedade: ```typescript const v = { x: 3 as const, }; v.x = 3; ``` Em um objeto inteiro: ```typescript const v = { x: 1, y: 2, } as const; ``` Isso pode ser particularmente útil ao definir o tipo para uma tupla: ```typescript const x = [1, 2, 3]; // number[] const y = [1, 2, 3] as const; // Tupla de readonly [1, 2, 3] ``` ### Anotação de Tipo Explícita Podemos ser específicos e passar um tipo; no exemplo a seguir, a propriedade `x` é do tipo `number`: ```typescript const v = { x: 1, // Tipo inferido: number (alargamento) }; v.x = 3; // Válido ``` Podemos tornar a anotação de tipo mais específica usando uma união de tipos literais: ```typescript const v: { x: 1 | 2 | 3 } = { x: 1, // x agora é uma união de tipos literais: 1 | 2 | 3 }; v.x = 3; // Válido v.x = 100; // Inválido ``` ### Estreitamento de Tipo (Type Narrowing) O Estreitamento de Tipo (Type Narrowing) é o processo no TypeScript onde um tipo geral é estreitado para um tipo mais específico. Isso ocorre quando o TypeScript analisa o código e determina que certas condições ou operações podem refinar a informação do tipo. O estreitamento de tipos pode ocorrer de diferentes maneiras, incluindo: #### Condições Ao usar instruções condicionais, como `if` ou `switch`, o TypeScript pode estreitar o tipo com base no resultado da condição. Por exemplo: ```typescript let x: number | undefined = 10; if (x !== undefined) { x += 100; // O tipo é number, que foi estreitado pela condição } ``` #### Lançando ou retornando Lançar um erro ou retornar cedo de uma ramificação pode ser usado para ajudar o TypeScript a estreitar um tipo. Por exemplo: ```typescript let x: number | undefined = 10; if (x === undefined) { throw 'erro'; } x += 100; ``` Outras formas de estreitar tipos no TypeScript incluem: * Operador `instanceof`: Usado para verificar se um objeto é uma instância de uma classe específica. * Operador `in`: Usado para verificar se uma propriedade existe em um objeto. * Operador `typeof`: Usado para verificar o tipo de um valor em tempo de execução. * Funções integradas como `Array.isArray()`: Usadas para verificar se um valor é um array. #### União Discriminada O uso de uma "União Discriminada" é um padrão no TypeScript onde uma "tag" explícita é adicionada aos objetos para distinguir entre diferentes tipos dentro de uma união. Este padrão também é conhecido como "união tagueada" (tagged union). No exemplo a seguir, a "tag" é representada pela propriedade "type": ```typescript type A = { type: 'type_a'; value: number }; type B = { type: 'type_b'; value: string }; const x = (input: A | B): string | number => { switch (input.type) { case 'type_a': return input.value + 100; // o tipo é A case 'type_b': return input.value + 'extra'; // o tipo é B } }; ``` #### Proteções de Tipo Definidas pelo Usuário (User-Defined Type Guards) Em casos onde o TypeScript não é capaz de determinar um tipo, é possível escrever uma função auxiliar conhecida como "proteção de tipo definida pelo usuário" (user-defined type guard). No exemplo a seguir, utilizaremos um Predicado de Tipo para estreitar o tipo após aplicar certa filtragem: ```typescript const data = ['a', null, 'c', 'd', null, 'f']; const r1 = data.filter(x => x != null); // O tipo é (string | null)[], o TypeScript não foi capaz de inferir o tipo corretamente const isValid = (item: string | null): item is string => item !== null; // Protetor de tipo customizado const r2 = data.filter(isValid); // O tipo está correto agora string[], ao usar o protetor de tipo predicado conseguimos estreitar o tipo ``` ## Tipos Primitivos O TypeScript suporta 7 tipos primitivos. Um tipo de dado primitivo refere-se a um tipo que não é um objeto e não possui nenhum método associado a ele. No TypeScript, todos os tipos primitivos são imutáveis, o que significa que seus valores não podem ser alterados uma vez que são atribuídos. ### string O tipo primitivo `string` armazena dados textuais, e o valor é sempre delimitado por aspas duplas ou simples. ```typescript const x: string = 'x'; const y: string = 'y'; ``` As strings podem abranger várias linhas se estiverem rodeadas pelo caractere de crase (`): ```typescript let sentence: string = `xxx, yyy`; ``` ### boolean O tipo de dado `boolean` no TypeScript armazena um valor binário, seja `true` ou `false`. ```typescript const isReady: boolean = true; ``` ### number Um tipo de dado `number` no TypeScript é representado com um valor de ponto flutuante de 64 bits. Um tipo `number` pode representar inteiros e frações. O TypeScript também suporta hexadecimal, binário e octal, por exemplo: ```typescript const decimal: number = 10; const hexadecimal: number = 0xa00d; // Hexadecimal começa com 0x const binary: number = 0b1010; // Binário começa com 0b const octal: number = 0o633; // Octal começa com 0o ``` ### bigInt Um `bigInt` representa valores numéricos muito grandes (253 – 1) e que não podem ser representados com um `number`. Um `bigInt` pode ser criado chamando a função integrada `BigInt()` ou adicionando `n` ao final de qualquer literal numérico inteiro: ```typescript const x: bigint = BigInt(9007199254740991); const y: bigint = 9007199254740991n; ``` Notas: * Valores `bigInt` não podem ser misturados com `number` e não podem ser usados com a função integrada `Math`; eles devem ser coagidos para o mesmo tipo. * Valores `bigInt` estão disponíveis apenas se a configuração da meta (target) for ES2020 ou superior. ### Symbol Symbols são identificadores únicos que podem ser usados como chaves de propriedade em objetos para evitar conflitos de nomenclatura. ```typescript type Obj = { [sym: symbol]: number; }; const a = Symbol('a'); const b = Symbol('b'); let obj: Obj = {}; obj[a] = 123; obj[b] = 456; console.log(obj[a]); // 123 console.log(obj[b]); // 456 ``` ### null e undefined Os tipos `null` e `undefined` representam a ausência de valor. O tipo `undefined` significa que o valor não foi atribuído ou inicializado, ou indica uma ausência não intencional de valor. O tipo `null` significa que sabemos que o campo não possui um valor, portanto o valor está indisponível; indica uma ausência intencional de valor. ### Array Um `array` é um tipo de dado que pode armazenar múltiplos valores do mesmo tipo ou não. Ele pode ser definido usando a seguinte sintaxe: ```typescript const x: string[] = ['a', 'b']; const y: Array = ['a', 'b']; const j: Array = ['a', 1, 'b', 2]; // União ``` O TypeScript suporta arrays somente leitura (readonly) usando a seguinte sintaxe: ```typescript const x: readonly string[] = ['a', 'b']; // Modificador readonly const y: ReadonlyArray = ['a', 'b']; const j: ReadonlyArray = ['a', 1, 'b', 2]; j.push('x'); // Inválido ``` O TypeScript suporta tupla e tupla somente leitura: ```typescript const x: [string, number] = ['a', 1]; const y: readonly [string, number] = ['a', 1]; ``` ### any O tipo de dado `any` representa literalmente "qualquer" valor; é o valor padrão quando o TypeScript não consegue inferir o tipo ou quando este não é especificado. Ao usar `any`, o compilador TypeScript ignora a verificação de tipo, portanto não há segurança de tipo quando o `any` está sendo usado. Geralmente, não use `any` para silenciar o compilador quando ocorre um erro; em vez disso, concentre-se em corrigir o erro, pois ao usar `any` é possível quebrar contratos e perdemos os benefícios do preenchimento automático do TypeScript. O tipo `any` pode ser útil durante uma migração gradual de JavaScript para TypeScript, pois pode silenciar o compilador. Para novos projetos, use a configuração do TypeScript `noImplicitAny`, que permite que o TypeScript emita erros onde `any` é usado ou inferido. O tipo `any` é geralmente uma fonte de erros que podem mascarar problemas reais com seus tipos. Evite usá-lo o máximo possível. ## Anotações de Tipo Em variáveis declaradas usando `var`, `let` e `const`, é possível adicionar opcionalmente um tipo: ```typescript const x: number = 1; ``` O TypeScript faz um bom trabalho ao inferir tipos, especialmente quando são simples, portanto essas declarações, na maioria dos casos, não são necessárias. Em funções, é possível adicionar anotações de tipo aos parâmetros: ```typescript function sum(a: number, b: number) { return a + b; } ``` O seguinte é um exemplo usando funções anônimas (as chamadas funções lambda): ```typescript const sum = (a: number, b: number) => a + b; ``` Essas anotações podem ser evitadas quando um valor padrão para um parâmetro está presente: ```typescript const sum = (a = 10, b: number) => a + b; ``` Anotações de tipo de retorno podem ser adicionadas às funções: ```typescript const sum = (a = 10, b: number): number => a + b; ``` Isso é útil especialmente para funções mais complexas, pois escrever explicitamente o tipo de retorno antes de uma implementação pode ajudar a pensar melhor sobre a função. Geralmente, considere anotar as assinaturas de tipo, mas não as variáveis locais do corpo, e sempre adicione tipos a objetos literais. ## Propriedades Opcionais Um objeto pode especificar Propriedades Opcionais adicionando um ponto de interrogação `?` ao final do nome da propriedade: ```typescript type X = { a: number; b?: number; // Opcional }; ``` É possível especificar um valor padrão quando uma propriedade é opcional: ```typescript type X = { a: number; b?: number; }; const x = ({ a, b = 100 }: X) => a + b; ``` ## Propriedades Somente Leitura (Readonly) É possível impedir a escrita em uma propriedade usando o modificador `readonly`, que garante que a propriedade não possa ser reescrita, mas não fornece nenhuma garantia de imutabilidade total: ```typescript interface Y { readonly a: number; } type X = { readonly a: number; }; type J = Readonly<{ a: number; }>; type K = { readonly [index: number]: string; }; ``` ## Assinaturas de Índice (Index Signatures) No TypeScript, podemos usar como assinatura de índice `string`, `number` e `symbol`: ```typescript type K = { [name: string | number]: string; }; const k: K = { x: 'x', 1: 'b' }; console.log(k['x']); console.log(k[1]); console.log(k['1']); // Mesmo resultado que k[1] ``` Observe que o JavaScript converte automaticamente um índice com `number` em um índice com `string`, portanto `k[1]` ou `k["1"]` retornam o mesmo valor. ## Estendendo Tipos É possível estender uma `interface` (copiar membros de outro tipo): ```typescript interface X { a: string; } interface Y extends X { b: string; } ``` Também é possível estender de múltiplos tipos: ```typescript interface A { a: string; } interface B { b: string; } interface Y extends A, B { y: string; } ``` A palavra-chave `extends` funciona apenas em interfaces e classes; para tipos, use uma interseção: ```typescript type A = { a: number; }; type B = { b: number; }; type C = A & B; ``` É possível estender um tipo usando uma inferência, mas não o contrário: ```typescript type A = { a: string; }; interface B extends A { b: string; } ``` ## Tipos Literais Um Tipo Literal é um conjunto de elemento único a partir de um tipo coletivo; ele define um valor exato que é um primitivo do JavaScript. Os Tipos Literais no TypeScript são números, strings e booleanos. Exemplo de literais: ```typescript const a = 'a'; // Tipo literal de string const b = 1; // Tipo literal numérico const c = true; // Tipo literal booleano ``` Tipos Literais de String, Numéricos e Booleanos são usados em uniões, protetores de tipo (type guards) e apelidos de tipo (type aliases). No exemplo a seguir, você pode ver um apelido de tipo de união. `O` consiste apenas nos valores especificados; nenhuma outra string é válida: ```typescript type O = 'a' | 'b' | 'c'; ``` ## Inferência Literal A Inferência Literal é um recurso do TypeScript que permite que o tipo de uma variável ou parâmetro seja inferido com base em seu valor. No exemplo a seguir, podemos ver que o TypeScript considera `x` um tipo literal, pois o valor não pode ser alterado posteriormente, enquanto `y` é inferido como string, pois pode ser modificado posteriormente. ```typescript const x = 'x'; // Tipo literal de 'x', porque este valor não pode ser alterado let y = 'y'; // Tipo string, pois podemos alterar este valor ``` No exemplo a seguir, podemos ver que `o.x` foi inferido como uma `string` (e não um literal de `a`), pois o TypeScript considera que o valor pode ser alterado posteriormente. ```typescript type X = 'a' | 'b'; let o = { x: 'a', // Esta é uma string mais ampla (wider string) }; const fn = (x: X) => `${x}-foo`; console.log(fn(o.x)); // Argument of type 'string' is not assignable to parameter of type 'X' ``` Como você pode observar, o código lança um erro ao passar `o.x` para `fn`, pois X é um tipo mais estreito (narrower). Podemos resolver este problema usando asserção de tipo com `const` ou o tipo `X`: ```typescript let o = { x: 'a' as const, }; ``` ou: ```typescript let o = { x: 'a' as X, }; ``` ## strictNullChecks `strictNullChecks` é uma opção do compilador TypeScript que impõe a verificação estrita de nulos. Quando esta opção está habilitada, variáveis e parâmetros só podem receber `null` | `undefined` se tiverem sido explicitamente declarados como sendo desse tipo usando o tipo de união `null` | `undefined`. Se uma variável ou parâmetro não for explicitamente declarado como anulável, o TypeScript gerará um erro para evitar possíveis erros de tempo de execução. ## Enums No TypeScript, um `enum` é um conjunto de valores constantes nomeados. ```typescript enum Color { Red = '#ff0000', Green = '#00ff00', Blue = '#0000ff', } ``` Enums podem ser definidos de diferentes maneiras: ### Enums numéricos No TypeScript, um Enum Numérico é um Enum onde cada constante recebe um valor numérico, começando em 0 por padrão. ```typescript enum Size { Small, // o valor começa de 0 Medium, Large, } ``` É possível especificar valores personalizados atribuindo-os explicitamente: ```typescript enum Size { Small = 10, Medium, Large, } console.log(Size.Medium); // 11 ``` ### Enums de string No TypeScript, um Enum de String é um Enum onde cada constante recebe um valor de string. ```typescript enum Language { English = 'EN', Spanish = 'ES', } ``` Nota: O TypeScript permite o uso de Enums heterogêneos, onde membros de string e numéricos podem coexistir. ### Enums constantes Um enum constante (const enum) no TypeScript é um tipo especial de Enum onde todos os valores são conhecidos em tempo de compilação e são inseridos diretamente (inlined) onde quer que o enum seja usado, resultando em um código mais eficiente. ```typescript const enum Language { English = 'EN', Spanish = 'ES', } console.log(Language.English); ``` Será compilado para: ```typescript console.log('EN' /* Language.English */); ``` Notas: Enums Constantes têm valores fixos (hardcoded), apagando o Enum, o que pode ser mais eficiente em bibliotecas autocontidas, mas geralmente não é desejável. Além disso, enums constantes não podem ter membros computados. ### Mapeamento reverso No TypeScript, os mapeamentos reversos em Enums referem-se à capacidade de recuperar o nome do membro do Enum a partir de seu valor. Por padrão, os membros do Enum têm mapeamentos diretos (forward mappings) do nome para o valor, mas mapeamentos reversos podem ser criados definindo explicitamente os valores para cada membro. Os mapeamentos reversos são úteis quando você precisa procurar um membro do Enum pelo seu valor ou quando precisa iterar sobre todos os membros do Enum. Note que apenas membros de enums numéricos gerarão mapeamentos reversos, enquanto membros de Enums de String não possuem um mapeamento reverso gerado. O seguinte enum: ```typescript enum Grade { A = 90, B = 80, C = 70, F = 'fail', } ``` Compila para: ```javascript 'use strict'; var Grade; (function (Grade) { Grade[(Grade['A'] = 90)] = 'A'; Grade[(Grade['B'] = 80)] = 'B'; Grade[(Grade['C'] = 70)] = 'C'; Grade['F'] = 'fail'; })(Grade || (Grade = {})); ``` Portanto, mapear valores para chaves funciona para membros de enums numéricos, mas não para membros de enums de string: ```typescript enum Grade { A = 90, B = 80, C = 70, F = 'fail', } const myGrade = Grade.A; console.log(Grade[myGrade]); // A console.log(Grade[90]); // A const failGrade = Grade.F; console.log(failGrade); // fail console.log(Grade[failGrade]); // Element implicitly has an 'any' type because index expression is not of type 'number'. ``` ### Enums de ambiente Um enum de ambiente no TypeScript é um tipo de Enum que é definido em um arquivo de declaração (*.d.ts) sem uma implementação associada. Ele permite definir um conjunto de constantes nomeadas que podem ser usadas de forma segura em relação aos tipos em diferentes arquivos, sem ter que importar os detalhes da implementação em cada arquivo. ### Membros computados e constantes No TypeScript, um membro computado é um membro de um Enum que possui um valor calculado em tempo de execução, enquanto um membro constante é um membro cujo valor é definido em tempo de compilação e não pode ser alterado durante o tempo de execução. Membros computados são permitidos em Enums regulares, enquanto membros constantes são permitidos tanto em enums regulares quanto em enums constantes (const enums). ```typescript // Membros constantes enum Color { Red = 1, Green = 5, Blue = Red + Green, } console.log(Color.Blue); // geração 6 em tempo de compilação ``` ```typescript // Membros computados enum Color { Red = 1, Green = Math.pow(2, 2), Blue = Math.floor(Math.random() * 3) + 1, } console.log(Color.Blue); // número aleatório gerado em tempo de execução ``` Enums são representados por uniões compostas pelos tipos de seus membros. Os valores de cada membro podem ser determinados por meio de expressões constantes ou não constantes, com membros que possuem valores constantes recebendo tipos literais. Para ilustrar, considere a declaração do tipo E e seus subtipos E.A, E.B e E.C. Neste caso, E representa a união E.A | E.B | E.C. ```typescript const identity = (value: number) => value; enum E { A = 2 * 5, // Literal numérico B = 'bar', // Literal de string C = identity(42), // Computado opaco } console.log(E.C); //42 ``` ## Estreitamento (Narrowing) O estreitamento (narrowing) no TypeScript é o processo de refinar o tipo de uma variável dentro de um bloco condicional. Isso é útil ao trabalhar com tipos de união, onde uma variável pode ter mais de um tipo. O TypeScript reconhece várias maneiras de estreitar o tipo: ### typeof type guards O protetor de tipo (type guard) `typeof` é um protetor de tipo específico no TypeScript que verifica o tipo de uma variável com base em seu tipo JavaScript integrado. ```typescript const fn = (x: number | string) => { if (typeof x === 'number') { return x + 1; // x é number } return -1; }; ``` ### Estreitamento de veracidade (Truthiness narrowing) O estreitamento de veracidade (truthiness narrowing) no TypeScript funciona verificando se uma variável é verdadeira (truthy) ou falsa (falsy) para estreitar seu tipo adequadamente. ```typescript const toUpperCase = (name: string | null) => { if (name) { return name.toUpperCase(); } else { return null; } }; ``` ### Estreitamento de igualdade (Equality narrowing) O estreitamento de igualdade (equality narrowing) no TypeScript funciona verificando se uma variável é igual a um valor específico ou não, para estreitar seu tipo adequadamente. É usado em conjunto com instruções `switch` e operadores de igualdade como `===`, `!==`, `==` e `!=` para estreitar os tipos. ```typescript const checkStatus = (status: 'success' | 'error') => { switch (status) { case 'success': return true; case 'error': return null; } }; ``` ### Estreitamento com operador In O estreitamento com o operador `in` no TypeScript é uma forma de estreitar o tipo de uma variável com base na existência de uma propriedade dentro do tipo da variável. ```typescript type Dog = { name: string; breed: string; }; type Cat = { name: string; likesCream: boolean; }; const getAnimalType = (pet: Dog | Cat) => { if ('breed' in pet) { return 'dog'; } else { return 'cat'; } }; ``` ### Estreitamento com instanceof O estreitamento com o operador `instanceof` no TypeScript é uma forma de estreitar o tipo de uma variável com base em sua função construtora, verificando se um objeto é uma instância de uma determinada classe ou interface. ```typescript class Square { constructor(public width: number) {} } class Rectangle { constructor( public width: number, public height: number ) {} } function area(shape: Square | Rectangle) { if (shape instanceof Square) { return shape.width * shape.width; } else { return shape.width * shape.height; } } const square = new Square(5); const rectangle = new Rectangle(5, 10); console.log(area(square)); // 25 console.log(area(rectangle)); // 50 ``` ## Atribuições O estreitamento do TypeScript usando atribuições é uma forma de estreitar o tipo de uma variável com base no valor atribuído a ela. Quando uma variável recebe um valor, o TypeScript infere seu tipo com base no valor atribuído e estreita o tipo da variável para corresponder ao tipo inferido. ```typescript let value: string | number; value = 'hello'; if (typeof value === 'string') { console.log(value.toUpperCase()); } value = 42; if (typeof value === 'number') { console.log(value.toFixed(2)); } ``` ## Análise de Fluxo de Controle A Análise de Fluxo de Controle (Control Flow Analysis) no TypeScript é uma forma de analisar estaticamente o fluxo do código para inferir os tipos das variáveis, permitindo que o compilador estreite os tipos dessas variáveis conforme necessário, com base nos resultados da análise. Antes do TypeScript 4.4, a análise de fluxo de código só seria aplicada ao código dentro de uma instrução `if`, mas a partir do TypeScript 4.4, ela também pode ser aplicada a expressões condicionais e acessos a propriedades discriminantes referenciados indiretamente por meio de variáveis `const`. Por exemplo: ```typescript const f1 = (x: unknown) => { const isString = typeof x === 'string'; if (isString) { x.length; } }; const f2 = ( obj: { kind: 'foo'; foo: string } | { kind: 'bar'; bar: number } ) => { const isFoo = obj.kind === 'foo'; if (isFoo) { obj.foo; } else { obj.bar; } }; ``` Alguns exemplos onde o estreitamento não ocorre: ```typescript const f1 = (x: unknown) => { let isString = typeof x === 'string'; if (isString) { x.length; // Erro, sem estreitamento porque isString não é const } }; const f6 = ( obj: { kind: 'foo'; foo: string } | { kind: 'bar'; bar: number } ) => { const isFoo = obj.kind === 'foo'; obj = obj; if (isFoo) { obj.foo; // Erro, sem estreitamento porque obj é atribuído no corpo da função } }; ``` Notas: Até cinco níveis de indireção são analisados em expressões condicionais. ## Predicados de Tipo Predicados de Tipo (Type Predicates) no TypeScript são funções que retornam um valor booleano e são usadas para estreitar o tipo de uma variável para um tipo mais específico. ```typescript const isString = (value: unknown): value is string => typeof value === 'string'; const foo = (bar: unknown) => { if (isString(bar)) { console.log(bar.toUpperCase()); } else { console.log('não é uma string'); } }; ``` ## Uniões Discriminadas Uniões Discriminadas (Discriminated Unions) no TypeScript são um tipo de união que usa uma propriedade comum, conhecida como discriminante, para estreitar o conjunto de tipos possíveis para a união. ```typescript type Square = { kind: 'square'; // Discriminante size: number; }; type Circle = { kind: 'circle'; // Discriminante radius: number; }; type Shape = Square | Circle; const area = (shape: Shape) => { switch (shape.kind) { case 'square': return Math.pow(shape.size, 2); case 'circle': return Math.PI * Math.pow(shape.radius, 2); } }; const square: Square = { kind: 'square', size: 5 }; const circle: Circle = { kind: 'circle', radius: 2 }; console.log(area(square)); // 25 console.log(area(circle)); // 12.566370614359172 ``` ## O tipo never Quando uma variável é estreitada para um tipo que não pode conter nenhum valor, o compilador TypeScript inferirá que a variável deve ser do tipo `never`. Isso ocorre porque o Tipo Never representa um valor que nunca pode ser produzido. ```typescript const printValue = (val: string | number) => { if (typeof val === 'string') { console.log(val.toUpperCase()); } else if (typeof val === 'number') { console.log(val.toFixed(2)); } else { // val tem o tipo never aqui porque nunca pode ser nada além de uma string ou um número const neverVal: never = val; console.log(`Valor inesperado: ${neverVal}`); } }; ``` ## Verificação de exaustividade A verificação de exaustividade (exhaustiveness checking) é um recurso no TypeScript que garante que todos os casos possíveis de uma união discriminada sejam tratados em uma instrução `switch` ou `if`. ```typescript type Direction = 'up' | 'down'; const move = (direction: Direction) => { switch (direction) { case 'up': console.log('Movendo para cima'); break; case 'down': console.log('Movendo para baixo'); break; default: const exhaustiveCheck: never = direction; console.log(exhaustiveCheck); // Esta linha nunca será executada } }; ``` O tipo `never` é usado para garantir que o caso `default` seja exaustivo e que o TypeScript aponte um erro se um novo valor for adicionado ao tipo `Direction` sem ser tratado na instrução switch. ## Tipos de Objeto No TypeScript, os tipos de objeto descrevem a forma de um objeto. Eles especificam os nomes e tipos das propriedades do objeto, bem como se essas propriedades são obrigatórias ou opcionais. No TypeScript, você pode definir tipos de objeto de duas maneiras principais: Interface, que define a forma de um objeto especificando os nomes, tipos e a opcionalidade de suas propriedades. ```typescript interface User { name: string; age: number; email?: string; } ``` Apelido de tipo (type alias), semelhante a uma interface, define a forma de um objeto. No entanto, ele também pode criar um novo tipo personalizado baseado em um tipo existente ou em uma combinação de tipos existentes. Isso inclui definir tipos de união, tipos de interseção e outros tipos complexos. ```typescript type Point = { x: number; y: number; }; ``` Também é possível definir um tipo anonimamente: ```typescript const sum = (x: { a: number; b: number }) => x.a + x.b; console.log(sum({ a: 5, b: 1 })); ``` ## Tipo Tupla (Anônimo) Um Tipo Tupla (Tuple Type) é um tipo que representa um array com um número fixo de elementos e seus tipos correspondentes. Um tipo tupla impõe um número específico de elementos e seus respectivos tipos em uma ordem fixa. Os tipos tupla são úteis quando você deseja representar uma coleção de valores com tipos específicos, onde a posição de cada elemento no array tem um significado específico. ```typescript type Point = [number, number]; ``` ## Tipo Tupla Nomeado (Rotulado) Os tipos tupla podem incluir rótulos (labels) ou nomes opcionais para cada elemento. Esses rótulos são para legibilidade e assistência de ferramentas, e não afetam as operações que você pode realizar com eles. ```typescript type T = string; type Tuple1 = [T, T]; type Tuple2 = [a: T, b: T]; type Tuple3 = [a: T, T]; // Tupla Nomeada mais Tupla Anônima ``` ## Tupla de Comprimento Fixo Uma Tupla de Comprimento Fixo é um tipo específico de tupla que impõe um número fixo de elementos de tipos específicos e proíbe quaisquer modificações no comprimento da tupla uma vez definida. Tuplas de Comprimento Fixo são úteis quando você precisa representar uma coleção de valores com um número específico de elementos e tipos específicos, e deseja garantir que o comprimento e os tipos da tupla não possam ser alterados inadvertidamente. ```typescript const x = [10, 'hello'] as const; x.push(2); // Erro ``` ## Tipo União Um Tipo União (Union Type) é um tipo que representa um valor que pode ser um de vários tipos. Tipos União são denotados usando o símbolo `|` entre cada tipo possível. ```typescript let x: string | number; x = 'hello'; // Válido x = 123; // Válido ``` ## Tipos de Interseção Um Tipo de Interseção (Intersection Type) é um tipo que representa um valor que possui todas as propriedades de dois ou mais tipos. Tipos de Interseção são denotados usando o símbolo `&` entre cada tipo. ```typescript type X = { a: string; }; type Y = { b: string; }; type J = X & Y; // Interseção const j: J = { a: 'a', b: 'b', }; ``` ## Indexação de Tipo Indexação de tipo (type indexing) refere-se à capacidade de definir tipos que podem ser indexados por uma chave não conhecida antecipadamente, usando uma assinatura de índice para especificar o tipo para propriedades que não são declaradas explicitamente. ```typescript type Dictionary = { [key: string]: T; }; const myDict: Dictionary = { a: 'a', b: 'b' }; console.log(myDict['a']); // Retorna a ``` ## Tipo a partir de Valor Tipo a partir de Valor (Type from Value) no TypeScript refere-se à inferência automática de um tipo a partir de um valor ou expressão através da inferência de tipos. ```typescript const x = 'x'; // O TypeScript infere 'x' como um literal de string com 'const' (imutável), mas alarga para 'string' com 'let' (atribuível novamente). ``` ## Tipo a partir de Retorno de Função Tipo a partir de Retorno de Função refere-se à capacidade de inferir automaticamente o tipo de retorno de uma função com base em sua implementação. Isso permite que o TypeScript determine o tipo do valor retornado pela função sem anotações de tipo explícitas. ```typescript const add = (x: number, y: number) => x + y; // O TypeScript pode inferir que o tipo de retorno da função é um número ``` ## Tipo a partir de Módulo Tipo a partir de Módulo refere-se à capacidade de usar os valores exportados de um módulo para inferir automaticamente seus tipos. Quando um módulo exporta um valor com um tipo específico, o TypeScript pode usar essa informação para inferir automaticamente o tipo desse valor quando ele é importado para outro módulo. ```typescript // calc.ts export const add = (x: number, y: number) => x + y; // index.ts import { add } from 'calc'; const r = add(1, 2); // r é number ``` ## Tipos Mapeados Tipos Mapeados (Mapped Types) no TypeScript permitem criar novos tipos baseados em um tipo existente, transformando cada propriedade por meio de uma função de mapeamento. Ao mapear tipos existentes, você pode criar novos tipos que representam a mesma informação em um formato diferente. Para criar um tipo mapeado, você acessa as propriedades de um tipo existente usando o operador `keyof` e as altera para produzir um novo tipo. No exemplo a seguir: ```typescript type MyMappedType = { [P in keyof T]: T[P][]; }; type MyType = { foo: string; bar: number; }; type MyNewType = MyMappedType; const x: MyNewType = { foo: ['hello', 'world'], bar: [1, 2, 3], }; ``` definimos `MyMappedType` para mapear sobre as propriedades de `T`, criando um novo tipo com cada propriedade sendo um array de seu tipo original. Usando isso, criamos `MyNewType` para representar a mesma informação que `MyType`, mas com cada propriedade como um array. ## Modificadores de Tipos Mapeados Os Modificadores de Tipos Mapeados no TypeScript permitem a transformação de propriedades dentro de um tipo existente: * `readonly` ou `+readonly`: Torna uma propriedade no tipo mapeado como somente leitura. * `-readonly`: Permite que uma propriedade no tipo mapeado seja mutável. * `?`: Designa uma propriedade no tipo mapeado como opcional. Exemplos: ```typescript type ReadOnly = { readonly [P in keyof T]: T[P] }; // Todas as propriedades marcadas como somente leitura type Mutable = { -readonly [P in keyof T]: T[P] }; // Todas as propriedades marcadas como mutáveis type MyPartial = { [P in keyof T]?: T[P] }; // Todas as propriedades marcadas como opcionais ``` ## Tipos Condicionais (Conditional Types) Tipos Condicionais são uma forma de criar um tipo que depende de uma condição, onde o tipo a ser criado é determinado com base no resultado da condição. Eles são definidos usando a palavra-chave `extends` e um operador ternário para escolher condicionalmente entre dois tipos. ```typescript type IsArray = T extends any[] ? true : false; const myArray = [1, 2, 3]; const myNumber = 42; type IsMyArrayAnArray = IsArray; // Tipo true type IsMyNumberAnArray = IsArray; // Tipo false ``` ## Tipos Condicionais Distributivos Tipos Condicionais Distributivos são um recurso que permite que um tipo seja distribuído sobre uma união de tipos, aplicando uma transformação a cada membro da união individualmente. Isso pode ser especialmente útil ao trabalhar com tipos mapeados ou tipos de ordem superior. ```typescript type Nullable = T extends any ? T | null : never; type NumberOrBool = number | boolean; type NullableNumberOrBool = Nullable; // number | boolean | null ``` ## infer Inferência de Tipo em Tipos Condicionais A palavra-chave `infer` é usada em tipos condicionais para inferir (extrair) o tipo de um parâmetro genérico de um tipo que depende dele. Isso permite escrever definições de tipo mais flexíveis e reutilizáveis. ```typescript type ElementType = T extends (infer U)[] ? U : never; type Numbers = ElementType; // number type Strings = ElementType; // string ``` ## Tipos Condicionais Predefinidos No TypeScript, os Tipos Condicionais Predefinidos são tipos condicionais integrados fornecidos pela linguagem. Eles são projetados para realizar transformações comuns de tipo com base nas características de um determinado tipo. `Exclude`: Este tipo remove todos os tipos de Type que são atribuíveis a ExcludedType. `Extract`: Este tipo extrai todos os tipos de Union que são atribuíveis a Type. `NonNullable`: Este tipo remove null e undefined de Type. `ReturnType`: Este tipo extrai o tipo de retorno de uma função Type. `Parameters`: Este tipo extrai os tipos de parâmetros de uma função Type. `Required`: Este tipo torna todas as propriedades em Type obrigatórias. `Partial`: Este tipo torna todas as propriedades em Type opcionais. `Readonly`: Este tipo torna todas as propriedades em Type somente leitura (readonly). ## Tipos de União de Template (Template Union Types) Tipos de união de template podem ser usados para mesclar e manipular texto dentro do sistema de tipos, por exemplo: ```typescript type Status = 'active' | 'inactive'; type Products = 'p1' | 'p2'; type ProductId = `id-${Products}-${Status}`; // "id-p1-active" | "id-p1-inactive" | "id-p2-active" | "id-p2-inactive" ``` ## Tipo any O tipo `any` é um tipo especial (supertipo universal) que pode ser usado para representar qualquer tipo de valor (primitivos, objetos, arrays, funções, erros, símbolos). É frequentemente usado em situações onde o tipo de um valor não é conhecido em tempo de compilação, ou ao trabalhar com valores de APIs externas ou bibliotecas que não possuem tipagens TypeScript. Ao utilizar o tipo `any`, você está indicando ao compilador TypeScript que os valores devem ser representados sem quaisquer limitações. Para maximizar a segurança de tipo em seu código, considere o seguinte: * Limite o uso de `any` a casos específicos onde o tipo é verdadeiramente desconhecido. * Não retorne tipos `any` de uma função, pois isso enfraquece a segurança de tipo no código que a utiliza. * Em vez de `any`, use `@ts-ignore` se precisar silenciar o compilador. ```typescript let value: any; value = true; // Válido value = 7; // Válido ``` ## Tipo unknown No TypeScript, o tipo `unknown` representa um valor que é de um tipo desconhecido. Ao contrário do tipo `any`, que permite qualquer tipo de valor, o `unknown` exige uma verificação de tipo ou asserção antes de poder ser usado de uma maneira específica, portanto nenhuma operação é permitida em um `unknown` sem primeiro asseverar ou estreitar para um tipo mais específico. O tipo `unknown` só é atribuível a si mesmo e ao tipo `any`; é uma alternativa segura em termos de tipos ao `any`. ```typescript let value: unknown; let value1: unknown = value; // Válido let value2: any = value; // Válido let value3: boolean = value; // Inválido let value4: number = value; // Inválido ``` ```typescript const add = (a: unknown, b: unknown): number | undefined => typeof a === 'number' && typeof b === 'number' ? a + b : undefined; console.log(add(1, 2)); // 3 console.log(add('x', 2)); // undefined ``` ## Tipo void O tipo `void` é usado para indicar que uma função não retorna um valor. ```typescript const sayHello = (): void => { console.log('Hello!'); }; ``` ## Tipo never O tipo `never` representa valores que nunca ocorrem. É usado para denotar funções ou expressões que nunca retornam ou que lançam um erro. Por exemplo, um loop infinito: ```typescript const infiniteLoop = (): never => { while (true) { // faz algo } }; ``` Lançando um erro: ```typescript const throwError = (message: string): never => { throw new Error(message); }; ``` O tipo `never` é útil para garantir a segurança de tipos e capturar possíveis erros em seu código. Ele ajuda o TypeScript a analisar e inferir tipos mais precisos quando usado em combinação com outros tipos e instruções de fluxo de controle, por exemplo: ```typescript type Direction = 'up' | 'down'; const move = (direction: Direction): void => { switch (direction) { case 'up': // move para cima break; case 'down': // move para baixo break; default: const exhaustiveCheck: never = direction; throw new Error(`Direção não tratada: ${exhaustiveCheck}`); } }; ``` ## Interface e Type ### Sintaxe Comum No TypeScript, as interfaces definem a estrutura de objetos, especificando os nomes e tipos de propriedades ou métodos que um objeto deve possuir. A sintaxe comum para definir uma interface no TypeScript é a seguinte: ```typescript interface InterfaceName { property1: Type1; // ... method1(arg1: ArgType1, arg2: ArgType2): ReturnType; // ... } ``` Da mesma forma para a definição de tipo (type): ```typescript type TypeName = { property1: Type1; // ... method1(arg1: ArgType1, arg2: ArgType2): ReturnType; // ... }; ``` `interface InterfaceName` ou `type TypeName`: Define o nome da interface/tipo. `property1`: `Type1`: Especifica as propriedades da interface junto com seus tipos correspondentes. Múltiplas propriedades podem ser definidas, cada uma separada por um ponto e vírgula. `method1(arg1: ArgType1, arg2: ArgType2): ReturnType;`: Especifica os métodos da interface. Métodos são definidos com seus nomes, seguidos por uma lista de parâmetros entre parênteses e o tipo de retorno. Múltiplos métodos podem ser definidas, cada uma separada por um ponto e vírgula. Exemplo de interface: ```typescript interface Person { name: string; age: number; greet(): void; } ``` Exemplo de tipo: ```typescript type TypeName = { property1: string; method1(arg1: string, arg2: string): string; }; ``` No TypeScript, os tipos são usados para definir a forma dos dados e impor a verificação de tipos. Existem várias sintaxes comuns para definir tipos, dependendo do caso de uso específico. Aqui estão alguns exemplos: ### Tipos Básicos ```typescript let myNumber: number = 123; // tipo number let myBoolean: boolean = true; // tipo boolean let myArray: string[] = ['a', 'b']; // array de strings let myTuple: [string, number] = ['a', 123]; // tupla ``` ### Objetos e Interfaces ```typescript const x: { name: string; age: number } = { name: 'Simon', age: 7 }; ``` ### Tipos União e Interseção ```typescript type MyType = string | number; // Tipo União (Union type) let myUnion: MyType = 'hello'; // Pode ser uma string myUnion = 123; // Ou um número type TypeA = { name: string }; type TypeB = { age: number }; type CombinedType = TypeA & TypeB; // Tipo Interseção (Intersection type) let myCombined: CombinedType = { name: 'John', age: 25 }; // Objeto com as propriedades name e age ``` ## Tipos Primitivos Integrados O TypeScript possui vários tipos primitivos integrados que podem ser usados para definir variáveis, parâmetros de função e tipos de retorno: * `number`: Representa valores numéricos, incluindo inteiros e números de ponto flutuante. * `string`: Representa dados textuais. * `boolean`: Representa valores lógicos, que podem ser true ou false. * `null`: Representa a ausência de um valor. * `undefined`: Representa um valor que não foi atribuído ou não foi definido. * `symbol`: Representa um identificador único. Symbols são normalmente usados como chaves para propriedades de objetos. * `bigint`: Representa inteiros de precisão arbitrária. * `any`: Representa um tipo dinâmico ou desconhecido. Variáveis do tipo any podem conter valores de qualquer tipo e ignoram a verificação de tipos. * `void`: Representa a ausência de qualquer tipo. É comumente usado como o tipo de retorno de funções que não retornam um valor. * `never`: Representa um tipo para valores que nunca ocorrem. É normalmente usado como o tipo de retorno de funções que lançam um erro ou entram em um loop infinito. ## Objetos JS Integrados Comuns O TypeScript é um superconjunto do JavaScript e inclui todos os objetos integrados do JavaScript comumente usados. Você pode encontrar uma lista extensa desses objetos no site de documentação da Mozilla Developer Network (MDN): [https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Global_Objects](https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Global_Objects) Aqui está uma lista de alguns objetos integrados do JavaScript comumente usados: * Function * Object * Boolean * Error * Number * BigInt * Math * Date * String * RegExp * Array * Map * Set * Promise * Intl ## Sobrecargas (Overloads) As sobrecargas de função (function overloads) no TypeScript permitem definir múltiplas assinaturas para um mesmo nome de função, permitindo que as funções sejam chamadas de diversas maneiras. Aqui está um exemplo: ```typescript // Sobrecargas function sayHi(name: string): string; function sayHi(names: string[]): string[]; // Implementação function sayHi(name: unknown): unknown { if (typeof name === 'string') { return `Hi, ${name}!`; } else if (Array.isArray(name)) { return name.map(name => `Hi, ${name}!`); } throw new Error('Valor inválido'); } sayHi('xx'); // Válido sayHi(['aa', 'bb']); // Válido ``` Aqui está outro exemplo de uso de sobrecargas de função dentro de uma `class`: ```typescript class Greeter { message: string; constructor(message: string) { this.message = message; } // sobrecarga sayHi(name: string): string; sayHi(names: string[]): ReadonlyArray; // implementação sayHi(name: unknown): unknown { if (typeof name === 'string') { return `${this.message}, ${name}!`; } else if (Array.isArray(name)) { return name.map(name => `${this.message}, ${name}!`); } throw new Error('o valor é inválido'); } } console.log(new Greeter('Hello').sayHi('Simon')); ``` ## Mesclagem e Extensão Mesclagem (merging) e extensão (extension) referem-se a dois conceitos diferentes relacionados ao trabalho com tipos e interfaces. A mesclagem permite combinar várias declarações do mesmo nome em uma única definição, por exemplo, quando você define uma interface com o mesmo nome várias vezes: ```typescript interface X { a: string; } interface X { b: number; } const person: X = { a: 'a', b: 7, }; ``` A extensão refere-se à capacidade de estender ou herdar de tipos ou interfaces existentes para criar novos. É um mecanismo para adicionar propriedades ou métodos adicionais a um tipo existente sem modificar sua definição original. Exemplo: ```typescript interface Animal { name: string; eat(): void; } interface Bird extends Animal { sing(): void; } const dog: Bird = { name: 'Bird 1', eat() { console.log('Comendo'); }, sing() { console.log('Cantando'); }, }; ``` ## Diferenças entre Type e Interface Mesclagem de declarações (aumento): As interfaces suportam a mesclagem de declarações, o que significa que você pode definir várias interfaces com o mesmo nome, e o TypeScript as mesclará em uma única interface com as propriedades e métodos combinados. Por outro lado, os tipos (types) não suportam a mesclagem de declarações. Isso pode ser útil quando você deseja adicionar funcionalidades extras ou personalizar tipos existentes sem modificar as definições originais ou corrigir tipos ausentes ou incorretos. ```typescript interface A { x: string; } interface A { y: string; } const j: A = { x: 'xx', y: 'yy', }; ``` Estendendo outros tipos/interfaces: Tanto tipos quanto interfaces podem estender outros tipos/interfaces, mas a sintaxe é diferente. Com as interfaces, você usa a palavra-chave `extends` para herdar propriedades e métodos de outras interfaces. No entanto, uma interface não pode estender um tipo complexo, como um tipo união. ```typescript interface A { x: string; y: number; } interface B extends A { z: string; } const car: B = { x: 'x', y: 123, z: 'z', }; ``` Para tipos, você usa o operador `&` para combinar múltiplos tipos em um único tipo (interseção). ```typescript interface A { x: string; y: number; } type B = A & { j: string; }; const c: B = { x: 'x', y: 123, j: 'j', }; ``` Tipos União e Interseção: Os tipos (types) são mais flexíveis quando se trata de definir Tipos União e Interseção. Com a palavra-chave `type`, você pode criar facilmente tipos união usando o operador `|` e tipos interseção usando o operador `&`. Embora as interfaces também possam representar tipos união indiretamente, elas não possuem suporte integrado para tipos interseção. ```typescript type Department = 'dep-x' | 'dep-y'; // União type Person = { name: string; age: number; }; type Employee = { id: number; department: Department; }; type EmployeeInfo = Person & Employee; // Interseção ``` Exemplo com interfaces: ```typescript interface A { x: 'x'; } interface B { y: 'y'; } type C = A | B; // União de interfaces ``` ## Classes ### Sintaxe Comum de Classes A palavra-chave `class` é usada no TypeScript para definir uma classe. Abaixo, você pode ver um exemplo: ```typescript class Person { private name: string; private age: number; constructor(name: string, age: number) { this.name = name; this.age = age; } public sayHi(): void { console.log( `Olá, meu nome é ${this.name} e eu tenho ${this.age} anos.` ); } } ``` A palavra-chave `class` é usada para definir uma classe chamada "Person". A classe possui duas propriedades privadas: name do tipo `string` e age do tipo `number`. O construtor é definido usando a palavra-chave `constructor`. Ele recebe name e age como parâmetros e os atribui às propriedades correspondentes. A classe possui um método `public` chamado `sayHi` que registra uma mensagem de saudação. Para criar uma instância de uma classe no TypeScript, você pode usar a palavra-chave `new` seguida pelo nome da classe, seguida de parênteses `()`. Por exemplo: ```typescript const myObject = new Person('John Doe', 25); myObject.sayHi(); // Saída: Olá, meu nome é John Doe e eu tenho 25 anos. ``` ### Construtor Os construtores são métodos especiais dentro de uma classe que são usados para inicializar as propriedades do objeto quando uma instância da classe é criada. ```typescript class Person { public name: string; public age: number; constructor(name: string, age: number) { this.name = name; this.age = age; } sayHello() { console.log( `Olá, meu nome é ${this.name} e eu tenho ${this.age} anos.` ); } } const john = new Person('Simon', 17); john.sayHello(); ``` É possível sobrecarregar um construtor usando a seguinte sintaxe: ```typescript type Sex = 'm' | 'f'; class Person { name: string; age: number; sex: Sex; constructor(name: string, age: number, sex?: Sex); constructor(name: string, age: number, sex: Sex) { this.name = name; this.age = age; this.sex = sex ?? 'm'; } } const p1 = new Person('Simon', 17); const p2 = new Person('Alice', 22, 'f'); ``` No TypeScript, é possível definir múltiplas sobrecargas de construtor, mas você pode ter apenas uma implementação que deve ser compatível com todas as sobrecargas, o que pode ser alcançado usando um parâmetro opcional. ```typescript class Person { name: string; age: number; constructor(); constructor(name: string); constructor(name: string, age: number); constructor(name?: string, age?: number) { this.name = name ?? 'Desconhecido'; this.age = age ?? 0; } displayInfo() { console.log(`Nome: ${this.name}, Idade: ${this.age}`); } } const person1 = new Person(); person1.displayInfo(); // Nome: Desconhecido, Idade: 0 const person2 = new Person('John'); person2.displayInfo(); // Nome: John, Idade: 0 const person3 = new Person('Jane', 25); person3.displayInfo(); // Nome: Jane, Idade: 25 ``` ### Construtores Privados e Protegidos No TypeScript, os construtores podem ser marcados como privados ou protegidos, o que restringe sua acessibilidade e uso. Construtores Privados (Private Constructors): Podem ser chamados apenas dentro da própria classe. Construtores privados são frequentemente usados em cenários onde você deseja impor um padrão singleton ou restringir a criação de instâncias a um método factual dentro da classe. Construtores Protegidos (Protected Constructors): Construtores protegidos são úteis quando você deseja criar uma classe base que não deve ser instanciada diretamente, mas pode ser estendida por subclasses. ```typescript class BaseClass { protected constructor() {} } class DerivedClass extends BaseClass { private value: number; constructor(value: number) { super(); this.value = value; } } // Tentar instanciar a classe base diretamente resultará em erro // const baseObj = new BaseClass(); // Erro: O construtor da classe 'BaseClass' é protegido. // Criar uma instância da classe derivada const derivedObj = new DerivedClass(10); ``` ### Modificadores de Acesso Modificadores de Acesso `private`, `protected` e `public` são usados para controlar a visibilidade e acessibilidade dos membros da classe, como propriedades e métodos, em classes TypeScript. Esses modificadores são essenciais para impor o encapsulamento e estabelecer limites para acessar e modificar o estado interno de uma classe. O modificador `private` restringe o acesso ao membro da classe apenas dentro da classe que o contém. O modificador `protected` permite o acesso ao membro da classe dentro da classe que o contém e suas classes derivadas. O modificador `public` fornece acesso irrestrito ao membro da classe, permitindo que ele seja acessado de qualquer lugar. ### Get e Set Getters e setters são métodos especiais que permitem definir comportamentos personalizados de acesso e modificação para propriedades de classe. Eles permitem encapsular o estado interno de um objeto e fornecer lógica adicional ao obter ou definir os valores das propriedades. No TypeScript, os getters e setters são definidos usando as palavras-chave `get` e `set`, respectivamente. Aqui está um exemplo: ```typescript class MyClass { private _myProperty: string; constructor(value: string) { this._myProperty = value; } get myProperty(): string { return this._myProperty; } set myProperty(value: string) { this._myProperty = value; } } ``` ### Auto-acessores em Classes O TypeScript versão 4.9 adiciona suporte para auto-acessores (auto-accessors), um recurso futuro do ECMAScript. Eles se assemelham a propriedades de classe, mas são declarados com a palavra-chave "accessor". ```typescript class Animal { accessor name: string; constructor(name: string) { this.name = name; } } ``` Os auto-acessores são transformados em acessores `get` e `set` privados, operando em uma propriedade inacessível. ```typescript class Animal { #__name: string; get name() { return this.#__name; } set name(value: string) { this.#__name = name; } constructor(name: string) { this.name = name; } } ``` ### this No TypeScript, a palavra-chave `this` refere-se à instância atual de uma classe dentro de seus métodos ou construtores. Ela permite acessar e modificar as propriedades e métodos da classe de dentro de seu próprio escopo. Fornece uma maneira de acessar e manipular o estado interno de um objeto dentro de seus próprios métodos. ```typescript class Person { private name: string; constructor(name: string) { this.name = name; } public introduce(): void { console.log(`Olá, meu nome é ${this.name}.`); } } const person1 = new Person('Alice'); person1.introduce(); // Olá, meu nome é Alice. ``` ### Propriedades de Parâmetro As propriedades de parâmetro permitem declarar e inicializar propriedades de classe diretamente dentro dos parâmetros do construtor, evitando o código repetitivo (boilerplate). Exemplo: ```typescript class Person { constructor( private name: string, public age: number ) { // As palavras-chave "private" e "public" no construtor // declaram e inicializam automaticamente as propriedades de classe correspondentes. } public introduce(): void { console.log( `Olá, meu nome é ${this.name} e eu tenho ${this.age} anos.` ); } } const person = new Person('Alice', 25); person.introduce(); ``` ### Classes Abstratas Classes Abstratas são usadas no TypeScript principalmente para herança; elas fornecem uma maneira de definir propriedades e métodos comuns que podem ser herdados por subclasses. Isso é útil quando você deseja definir um comportamento comum e garantir que as subclasses implementem certos métodos. Elas fornecem uma maneira de criar uma hierarquia de classes onde a classe base abstrata fornece uma interface compartilhada e funcionalidade comum para as subclasses. ```typescript abstract class Animal { protected name: string; constructor(name: string) { this.name = name; } abstract makeSound(): void; } class Cat extends Animal { makeSound(): void { console.log(`${this.name} mia (meows).`); } } const cat = new Cat('Whiskers'); cat.makeSound(); // Saída: Whiskers mia (meows). ``` ### Com Genéricos Classes com genéricos permitem definir classes reutilizáveis que podem trabalhar com diferentes tipos. ```typescript class Container { private item: T; constructor(item: T) { this.item = item; } getItem(): T { return this.item; } setItem(item: T): void { this.item = item; } } const container1 = new Container(42); console.log(container1.getItem()); // 42 const container2 = new Container('Olá'); container2.setItem('Mundo'); console.log(container2.getItem()); // Mundo ``` ### Decoradores (Decorators) Os decoradores fornecem um mecanismo para adicionar metadados, modificar comportamentos, validar ou estender a funcionalidade do elemento alvo. São funções que são executadas em tempo de execução. Múltiplos decoradores podem ser aplicados a uma declaração. Os decoradores são recursos experimentais, e os exemplos a seguir são compatíveis apenas com a versão 5 do TypeScript ou superior usando ES6. Para versões do TypeScript anteriores à 5, eles devem ser habilitados usando a propriedade `experimentalDecorators` em seu `tsconfig.json` ou usando `--experimentalDecorators` na sua linha de comando (mas o exemplo a seguir não funcionará). Alguns dos casos de uso comuns para decoradores incluem: * Observar mudanças de propriedade. * Observar chamadas de métodos. * Adicionar propriedades ou métodos extras. * Validação em tempo de execução. * Serialização e desserialização automática. * Registro (Logging). * Autorização e autenticação. * Proteção contra erros (Error guarding). Nota: Decoradores para a versão 5 não permitem decorar parâmetros. Tipos de decoradores: #### Decoradores de Classe (Class Decorators) Os Decoradores de Classe são úteis para estender uma classe existente, como adicionar propriedades ou métodos, ou coletar instâncias de uma classe. No exemplo a seguir, adicionamos um método `toString` que converte a classe em uma representação de string. ```typescript type Constructor = new (...args: any[]) => T; function toString( Value: Class, context: ClassDecoratorContext ) { return class extends Value { constructor(...args: any[]) { super(...args); console.log(JSON.stringify(this)); console.log(JSON.stringify(context)); } }; } @toString class Person { name: string; constructor(name: string) { this.name = name; } greet() { return 'Hello, ' + this.name; } } const person = new Person('Simon'); /* Registra: {"name":"Simon"} {"kind":"class","name":"Person"} */ ``` #### Decorador de Propriedade (Property Decorator) Os decoradores de propriedade são úteis para modificar o comportamento de uma propriedade, como alterar os valores de inicialização. No código a seguir, temos um script que define uma propriedade para estar sempre em letras maiúsculas: ```typescript function upperCase( target: undefined, context: ClassFieldDecoratorContext ) { return function (this: T, value: string) { return value.toUpperCase(); }; } class MyClass { @upperCase prop1 = 'hello!'; } console.log(new MyClass().prop1); // Registra: HELLO! ``` #### Decorador de Método (Method Decorator) Os decoradores de método permitem alterar ou aprimorar o comportamento dos métodos. Abaixo está um exemplo de um registrador (logger) simples: ```typescript function log( target: (this: This, ...args: Args) => Return, context: ClassMethodDecoratorContext< This, (this: This, ...args: Args) => Return > ) { const methodName = String(context.name); function replacementMethod(this: This, ...args: Args): Return { console.log(`LOG: Entrando no método '${methodName}'.`); const result = target.call(this, ...args); console.log(`LOG: Saindo do método '${methodName}'.`); return result; } return replacementMethod; } class MyClass { @log sayHello() { console.log('Hello!'); } } new MyClass().sayHello(); ``` Isso registra: ```shell LOG: Entrando no método 'sayHello'. Hello! LOG: Saindo do método 'sayHello'. ``` #### Decoradores de Getter e Setter Decoradores de getter e setter permitem alterar ou aprimorar o comportamento dos acessores de classe. Eles são úteis, por exemplo, para validar atribuições de propriedades. Aqui está um exemplo simples de um decorador de getter: ```typescript function range(min: number, max: number) { return function ( target: (this: This) => Return, context: ClassGetterDecoratorContext ) { return function (this: This): Return { const value = target.call(this); if (value < min || value > max) { throw 'Invalid'; } Object.defineProperty(this, context.name, { value, enumerable: true, }); return value; }; }; } class MyClass { private _value = 0; constructor(value: number) { this._value = value; } @range(1, 100) get getValue(): number { return this._value; } } const obj = new MyClass(10); console.log(obj.getValue); // Válido: 10 const obj2 = new MyClass(999); console.log(obj2.getValue); // Lança: Invalid! ``` #### Metadados de Decorador (Decorator Metadata) Os Metadados de Decorador simplificam o processo para que os decoradores apliquem e utilizem metadados em qualquer classe. Eles podem acessar uma nova propriedade de metadados no objeto de contexto, que pode servir como uma chave para primitivos e objetos. As informações de metadados podem ser acessadas na classe via `Symbol.metadata`. Os metadados podem ser usados para vários fins, como depuração, serialização ou injeção de dependência com decoradores. ```typescript //@ts-ignore Symbol.metadata ??= Symbol('Symbol.metadata'); // Polify simples type Context = | ClassFieldDecoratorContext | ClassAccessorDecoratorContext | ClassMethodDecoratorContext; // O contexto contém os metadados da propriedade: DecoratorMetadata function setMetadata(_target: any, context: Context) { // Define o objeto de metadados com um valor primitivo context.metadata[context.name] = true; } class MyClass { @setMetadata a = 123; @setMetadata accessor b = 'b'; @setMetadata fn() {} } const metadata = MyClass[Symbol.metadata]; // Obtém as informações de metadados console.log(JSON.stringify(metadata)); // {"bar":true,"baz":true,"foo":true} ``` ### Herança Herança refere-se ao mecanismo pelo qual uma classe pode herdar propriedades e métodos de outra classe, conhecida como classe base ou superclasse. A classe derivada, também chamada de classe filha ou subclasse, pode estender e especializar a funcionalidade da classe base adicionando novas propriedades e métodos ou substituindo (overriding) os existentes. ```typescript class Animal { name: string; constructor(name: string) { this.name = name; } speak(): void { console.log('O animal faz um som'); } } class Dog extends Animal { breed: string; constructor(name: string, breed: string) { super(name); this.breed = breed; } speak(): void { console.log('Woof! Woof!'); } } // Cria uma instância da classe base const animal = new Animal('Animal Genérico'); animal.speak(); // O animal faz um som // Cria uma instância da classe derivada const dog = new Dog('Max', 'Labrador'); dog.speak(); // Woof! Woof!" ``` O TypeScript não suporta herança múltipla no sentido tradicional e, em vez disso, permite a herança de uma única classe base. O TypeScript suporta múltiplas interfaces. Uma interface pode definir um contrato para a estrutura de um objeto, e uma classe pode implementar múltiplas interfaces. Isso permite que uma classe herde comportamento e estrutura de múltiplas fontes. ```typescript interface Flyable { fly(): void; } interface Swimmable { swim(): void; } class FlyingFish implements Flyable, Swimmable { fly() { console.log('Voando...'); } swim() { console.log('Nadando...'); } } const flyingFish = new FlyingFish(); flyingFish.fly(); flyingFish.swim(); ``` A palavra-chave `class` no TypeScript, assim como no JavaScript, é frequentemente chamada de açúcar sintático (syntactic sugar). Ela foi introduzida no ECMAScript 2015 (ES6) para oferecer uma sintaxe mais familiar para criar e trabalhar com objetos de maneira baseada em classes. No entanto, é importante notar que o TypeScript, sendo um superconjunto do JavaScript, acaba sendo compilado para JavaScript, que permanece baseado em protótipos em seu núcleo. ### Estáticos (Statics) O TypeScript possui membros estáticos. Para acessar os membros estáticos de uma classe, você pode usar o nome da classe seguido por um ponto, sem a necessidade de criar um objeto. ```typescript class OfficeWorker { static memberCount: number = 0; constructor(private name: string) { OfficeWorker.memberCount++; } } const w1 = new OfficeWorker('James'); const w2 = new OfficeWorker('Simon'); const total = OfficeWorker.memberCount; console.log(total); // 2 ``` ### Inicialização de Propriedade Existem várias maneiras de inicializar propriedades de uma classe no TypeScript: Em linha (Inline): No exemplo a seguir, esses valores iniciais serão usados quando uma instância da classe for criada. ```typescript class MyClass { property1: string = 'valor padrão'; property2: number = 42; } ``` No construtor: ```typescript class MyClass { property1: string; property2: number; constructor() { this.property1 = 'valor padrão'; this.property2 = 42; } } ``` Usando parâmetros do construtor: ```typescript class MyClass { constructor( private property1: string = 'valor padrão', public property2: number = 42 ) { // Não há necessidade de atribuir os valores às propriedades explicitamente. } log() { console.log(this.property2); } } const x = new MyClass(); x.log(); ``` ### Sobrecarga de Método A sobrecarga de método permite que uma classe tenha múltiplos métodos com o mesmo nome, mas diferentes tipos de parâmetros ou um número diferente de parâmetros. Isso nos permite chamar um método de diferentes maneiras com base nos argumentos passados. ```typescript class MyClass { add(a: number, b: number): number; // Assinatura de sobrecarga 1 add(a: string, b: string): string; // Assinatura de sobrecarga 2 add(a: number | string, b: number | string): number | string { if (typeof a === 'number' && typeof b === 'number') { return a + b; } if (typeof a === 'string' && typeof b === 'string') { return a.concat(b); } throw new Error('Argumentos inválidos'); } } const r = new MyClass(); console.log(r.add(10, 5)); // Registra 15 ``` ## Genéricos (Generics) Os genéricos permitem que você crie componentes e funções reutilizáveis que podem trabalhar com múltiplos tipos. Com os genéricos, você pode parametrizar tipos, funções e interfaces, permitindo que operem em diferentes tipos sem especificá-los explicitamente de antemão. Os genéricos permitem tornar o código mais flexível e reutilizável. ### Tipo Genérico Para definir um tipo genérico, você usa colchetes angulares (`<>`) para especificar os parâmetros de tipo, por exemplo: ```typescript function identity(arg: T): T { return arg; } const a = identity('x'); const b = identity(123); const getLen = (data: ReadonlyArray) => data.length; const len = getLen([1, 2, 3]); ``` ### Classes Genéricas Os genéricos também podem ser aplicados a classes, permitindo que trabalhem com múltiplos tipos por meio de parâmetros de tipo. Isso é útil para criar definições de classe reutilizáveis que podem operar em diferentes tipos de dados mantendo a segurança de tipo. ```typescript class Container { private item: T; constructor(item: T) { this.item = item; } getItem(): T { return this.item; } } const numberContainer = new Container(123); console.log(numberContainer.getItem()); // 123 const stringContainer = new Container('hello'); console.log(stringContainer.getItem()); // hello ``` ### Restrições Genéricas (Generic Constraints) Parâmetros genéricos podem ser restringidos usando a palavra-chave `extends` seguida por um tipo ou interface que o parâmetro de tipo deve satisfazer. No exemplo a seguir, T deve conter uma propriedade `length` para ser válido: ```typescript const printLen = (value: T): void => { console.log(value.length); }; printLen('Hello'); // 5 printLen([1, 2, 3]); // 3 printLen({ length: 10 }); // 10 printLen(123); // Inválido ``` Um recurso interessante de genéricos introduzido na versão 3.4 RC é a inferência de tipo de função de ordem superior, que introduziu argumentos de tipo genérico propagados: ```typescript declare function pipe( ab: (...args: A) => B, bc: (b: B) => C ): (...args: A) => C; declare function list(a: T): T[]; declare function box(x: V): { value: V }; const listBox = pipe(list, box); // (a: T) => { value: T[] } const boxList = pipe(box, list); // (x: V) => { value: V }[] ``` Essa funcionalidade permite uma programação de estilo sem pontos (pointfree) com segurança de tipo mais fácil, o que é comum na programação funcional. ### Estreitamento Contextual Genérico O estreitamento contextual (contextual narrowing) para genéricos é o mecanismo no TypeScript que permite ao compilador estreitar o tipo de um parâmetro genérico com base no contexto em que é usado. É útil ao trabalhar com tipos genéricos em declarações condicionais: ```typescript function process(value: T): void { if (typeof value === 'string') { // O valor é estreitado para o tipo 'string' console.log(value.length); } else if (typeof value === 'number') { // O valor é estreitado para o tipo 'number' console.log(value.toFixed(2)); } } process('hello'); // 5 process(3.14159); // 3.14 ``` ## Tipos Estruturais Apagados No TypeScript, os objetos não precisam corresponder a um tipo exato e específico. Por exemplo, se criarmos um objeto que satisfaça os requisitos de uma interface, podemos utilizar esse objeto em locais onde essa interface é necessária, mesmo que não haja uma conexão explícita entre eles. Exemplo: ```typescript type NameProp1 = { prop1: string; }; function log(x: NameProp1) { console.log(x.prop1); } const obj = { prop2: 123, prop1: 'Origin', }; log(obj); // Válido ``` ## Namespacing No TypeScript, os namespaces são usados para organizar o código em contêineres lógicos, evitando colisões de nomes e fornecendo uma maneira de agrupar códigos relacionados. O uso das palavras-chave `export` permite o acesso ao namespace em módulos externos. ```typescript export namespace MyNamespace { export interface MyInterface1 { prop1: boolean; } export interface MyInterface2 { prop2: string; } } const a: MyNamespace.MyInterface1 = { prop1: true, }; ``` ## Símbolos (Symbols) Símbolos são um tipo de dado primitivo que representa um valor imutável que é garantido ser globalmente único durante todo o tempo de execução do programa. Símbolos podem ser usados como chaves para propriedades de objetos e fornecem uma maneira de criar propriedades não enumeráveis. ```typescript const key1: symbol = Symbol('key1'); const key2: symbol = Symbol('key2'); const obj = { [key1]: 'value 1', [key2]: 'value 2', }; console.log(obj[key1]); // value 1 console.log(obj[key2]); // value 2 ``` Em WeakMaps e WeakSets, símbolos agora são permitidos como chaves. ## Diretivas Triple-Slash As diretivas triple-slash são comentários especiais que fornecem instruções ao compilador sobre como processar um arquivo. Essas diretivas começam com três barras consecutivas (`///`) e são normalmente colocadas no topo de um arquivo TypeScript e não têm efeitos no comportamento em tempo de execução. As diretivas triple-slash são usadas para referenciar dependências externas, especificar o comportamento de carregamento de módulos, habilitar/desabilitar certos recursos do compilador e muito mais. Alguns exemplos: Referenciando um arquivo de declaração: ```typescript /// ``` Indicar o formato do módulo: ```typescript /// ``` Habilitar opções do compilador, no exemplo a seguir, o modo estrito: ```typescript /// ``` ## Manipulação de Tipos ### Criando Tipos a partir de Tipos É possível criar novos tipos compondo, manipulando ou transformando tipos existentes. Tipos Interseção (`&`): Permitem combinar múltiplos tipos em um único tipo: ```typescript type A = { foo: number }; type B = { bar: string }; type C = A & B; // Interseção de A e B const obj: C = { foo: 42, bar: 'hello' }; ``` Tipos União (`|`): Permitem definir um tipo que pode ser um de vários tipos: ```typescript type Result = string | number; const value1: Result = 'hello'; const value2: Result = 42; ``` Tipos Mapeados: Permitem transformar as propriedades de um tipo existente para criar um novo tipo: ```typescript type Mutable = { readonly [P in keyof T]: T[P]; }; type Person = { name: string; age: number; }; type ImmutablePerson = Mutable; // As propriedades tornam-se somente leitura ``` Tipos Condicionais: Permitem criar tipos com base em algumas condições: ```typescript type ExtractParam = T extends (param: infer P) => any ? P : never; type MyFunction = (name: string) => number; type ParamType = ExtractParam; // string ``` ### Tipos de Acesso Indexado (Indexed Access Types) No TypeScript, é possível acessar e manipular os tipos de propriedades dentro de outro tipo usando um índice, `Type[Key]`. ```typescript type Person = { name: string; age: number; }; type AgeType = Person['age']; // number ``` ```typescript type MyTuple = [string, number, boolean]; type MyType = MyTuple[2]; // boolean ``` ### Tipos Utilitários (Utility Types) Vários tipos utilitários integrados podem ser usados para manipular tipos, abaixo uma lista dos mais comuns: #### Awaited\ Constrói um tipo que descompacta recursivamente tipos Promise. ```typescript type A = Awaited>; // string ``` #### Partial\ Constrói um tipo com todas as propriedades de T definidas como opcionais. ```typescript type Person = { name: string; age: number; }; type A = Partial; // { name?: string | undefined; age?: number | undefined; } ``` #### Required\ Constrói um tipo com todas as propriedades de T definidas como obrigatórias. ```typescript type Person = { name?: string; age?: number; }; type A = Required; // { name: string; age: number; } ``` #### Readonly\ Constrói um tipo com todas as propriedades de T definidas como somente leitura. ```typescript type Person = { name: string; age: number; }; type A = Readonly; const a: A = { name: 'Simon', age: 17 }; a.name = 'John'; // Inválido ``` #### Record\ Constrói um tipo com um conjunto de propriedades K do tipo T. ```typescript type Product = { name: string; price: number; }; const products: Record = { apple: { name: 'Apple', price: 0.5 }, banana: { name: 'Banana', price: 0.25 }, }; console.log(products.apple); // { name: 'Apple', price: 0.5 } ``` #### Pick\ Constrói um tipo selecionando as propriedades especificadas K de T. ```typescript type Product = { name: string; price: number; }; type Price = Pick; // { price: number; } ``` #### Omit\ Constrói um tipo omitindo as propriedades especificadas K de T. ```typescript type Product = { name: string; price: number; }; type Name = Omit; // { name: string; } ``` #### Exclude\ Constrói um tipo excluindo todos os valores do tipo U de T. ```typescript type Union = 'a' | 'b' | 'c'; type MyType = Exclude; // b ``` #### Extract\ Constrói um tipo extraindo todos os valores do tipo U de T. ```typescript type Union = 'a' | 'b' | 'c'; type MyType = Extract; // a | c ``` #### NonNullable\ Constrói um tipo excluindo null e undefined de T. ```typescript type Union = 'a' | null | undefined | 'b'; type MyType = NonNullable; // 'a' | 'b' ``` #### Parameters\ Extrai os tipos de parâmetros de um tipo de função T. ```typescript type Func = (a: string, b: number) => void; type MyType = Parameters; // [a: string, b: number] ``` #### ConstructorParameters\ Extrai os tipos de parâmetros de um tipo de função construtora T. ```typescript class Person { constructor( public name: string, public age: number ) {} } type PersonConstructorParams = ConstructorParameters; // [name: string, age: number] const params: PersonConstructorParams = ['John', 30]; const person = new Person(...params); console.log(person); // Person { name: 'John', age: 30 } ``` #### ReturnType\ Extrai o tipo de retorno de um tipo de função T. ```typescript type Func = (name: string) => number; type MyType = ReturnType; // number ``` #### InstanceType\ Extrai o tipo de instância de um tipo de classe T. ```typescript class Person { name: string; constructor(name: string) { this.name = name; } sayHello() { console.log(`Olá, meu nome é ${this.name}!`); } } type PersonInstance = InstanceType; const person: PersonInstance = new Person('John'); person.sayHello(); // Olá, meu nome é John! ``` #### ThisParameterType\ Extrai o tipo do parâmetro 'this' de um tipo de função T. ```typescript interface Person { name: string; greet(this: Person): void; } type PersonThisType = ThisParameterType; // Person ``` #### OmitThisParameter\ Remove o parâmetro 'this' de um tipo de função T. ```typescript function capitalize(this: String) { return this[0].toUpperCase + this.substring(1).toLowerCase(); } type CapitalizeType = OmitThisParameter; // () => string ``` #### ThisType\ Serve como um marcador para um tipo `this` contextual. ```typescript type Logger = { log: (error: string) => void; }; let helperFunctions: { [name: string]: Function } & ThisType = { hello: function () { this.log('some error'); // Válido, pois "log" faz parte de "this". this.update(); // Inválido }, }; ``` #### Uppercase\ Converte para maiúsculas o nome do tipo de entrada T. ```typescript type MyType = Uppercase<'abc'>; // "ABC" ``` #### Lowercase\ Converte para minúsculas o nome do tipo de entrada T. ```typescript type MyType = Lowercase<'ABC'>; // "abc" ``` #### Capitalize\ Coloca a primeira letra em maiúscula no nome do tipo de entrada T. ```typescript type MyType = Capitalize<'abc'>; // "Abc" ``` #### Uncapitalize\ Coloca a primeira letra em minúscula no nome do tipo de entrada T. ```typescript type MyType = Uncapitalize<'Abc'>; // "abc" ``` #### NoInfer\ `NoInfer` é um tipo utilitário projetado para bloquear a inferência automática de tipos dentro do escopo de uma função genérica. Exemplo: ```typescript // Inferência automática de tipos dentro do escopo de uma função genérica. function fn(x: T[], y: T) { return x.concat(y); } const r = fn(['a', 'b'], 'c'); // O tipo aqui é ("a" | "b" | "c")[] ``` Com NoInfer: ```typescript // Exemplo de função que usa NoInfer para evitar inferência de tipo function fn2(x: T[], y: NoInfer) { return x.concat(y); } const r2 = fn2(['a', 'b'], 'c'); // Erro: Argumento de tipo '"c"' não é atribuível ao parâmetro do tipo '"a" | "b"'. ``` ## Outros ### Erros e Tratamento de Exceções O TypeScript permite capturar e tratar erros usando os mecanismos padrão de tratamento de erros do JavaScript: Blocos Try-Catch-Finally: ```typescript try { // Código que pode lançar um erro } catch (error) { // Trata o erro } finally { // Código que sempre executa, finally é opcional } ``` Você também pode tratar diferentes tipos de erro: ```typescript try { // Código que pode lançar diferentes tipos de erros } catch (error) { if (error instanceof TypeError) { // Trata TypeError } else if (error instanceof RangeError) { // Trata RangeError } else { // Trata outros erros } } ``` Tipos de Erro Personalizados: É possível especificar erros mais específicos estendendo a `class` Error: ```typescript class CustomError extends Error { constructor(message: string) { super(message); this.name = 'CustomError'; } } throw new CustomError('Este é um erro personalizado.'); ``` ### Classes Mixin (Mixin classes) As classes mixin permitem combinar e compor comportamentos de múltiplas classes em uma única classe. Elas fornecem uma maneira de reutilizar e estender funcionalidades sem a necessidade de cadeias de herança profundas. ```typescript abstract class Identifiable { name: string = ''; logId() { console.log('id:', this.name); } } abstract class Selectable { selected: boolean = false; select() { this.selected = true; console.log('Select'); } deselect() { this.selected = false; console.log('Deselect'); } } class MyClass { constructor() {} } // Estende MyClass para incluir o comportamento de Identifiable e Selectable interface MyClass extends Identifiable, Selectable {} // Função para aplicar mixins a uma classe function applyMixins(source: any, baseCtors: any[]) { baseCtors.forEach(baseCtor => { Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => { let descriptor = Object.getOwnPropertyDescriptor( baseCtor.prototype, name ); if (descriptor) { Object.defineProperty(source.prototype, name, descriptor); } }); }); } // Aplica os mixins a MyClass applyMixins(MyClass, [Identifiable, Selectable]); let o = new MyClass(); o.name = 'abc'; o.logId(); o.select(); ``` ### Recursos de Linguagem Assíncronos Como o TypeScript é um superconjunto do JavaScript, ele possui recursos de linguagem assíncronos integrados do JavaScript como: Promises: Promises são uma maneira de lidar com operações assíncronas e seus resultados usando métodos como `.then()` e `.catch()` para lidar com condições de sucesso e erro. Para saber mais: [https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Global_Objects/Promise](https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Global_Objects/Promise) Async/await: As palavras-chave async/await são uma maneira de fornecer uma sintaxe com aparência mais síncrona para trabalhar com Promises. A palavra-chave `async` é usada para definir uma função assíncrona, e a palavra-chave `await` é usada dentro de uma função async para pausar a execução até que uma Promise seja resolvida ou rejeitada. Para saber mais: [https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Statements/async_function](https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Statements/async_function) [https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Operators/await](https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Operators/await) As seguintes APIs são bem suportadas no TypeScript: Fetch API: [https://developer.mozilla.org/pt-BR/docs/Web/API/Fetch_API](https://developer.mozilla.org/pt-BR/docs/Web/API/Fetch_API) Web Workers: [https://developer.mozilla.org/pt-BR/docs/Web/API/Web_Workers_API](https://developer.mozilla.org/pt-BR/docs/Web/API/Web_Workers_API) Shared Workers: [https://developer.mozilla.org/pt-BR/docs/Web/API/SharedWorker](https://developer.mozilla.org/pt-BR/docs/Web/API/SharedWorker) WebSocket: [https://developer.mozilla.org/pt-BR/docs/Web/API/WebSockets_API](https://developer.mozilla.org/pt-BR/docs/Web/API/WebSockets_API) ### Iteradores e Geradores Tanto Iteradores quanto Geradores são bem suportados no TypeScript. Iteradores são objetos que implementam o protocolo iterador, fornecendo uma maneira de acessar elementos de uma coleção ou sequência um por um. É uma estrutura que contém um ponteiro para o próximo elemento na iteração. Eles possuem um método `next()` que retorna o próximo valor na sequência junto com um booleano indicando se a sequência terminou (`done`). ```typescript class NumberIterator implements Iterable { private current: number; constructor( private start: number, private end: number ) { this.current = start; } public next(): IteratorResult { if (this.current <= this.end) { const value = this.current; this.current++; return { value, done: false }; } else { return { value: undefined, done: true }; } } [Symbol.iterator](): Iterator { return this; } } const iterator = new NumberIterator(1, 3); for (const num of iterator) { console.log(num); } ``` Geradores são funções especiais definidas usando a sintaxe `function*` que simplifica a criação de iteradores. Eles usam a palavra-chave `yield` para definir a sequência de valores e pausar e retomar automaticamente a execução quando os valores são solicitados. Geradores facilitam a criação de iteradores e são especialmente úteis para trabalhar com sequências grandes ou infinitas. Exemplo: ```typescript function* numberGenerator(start: number, end: number): Generator { for (let i = start; i <= end; i++) { yield i; } } const generator = numberGenerator(1, 5); for (const num of generator) { console.log(num); } ``` O TypeScript também suporta iteradores assíncronos e geradores assíncronos. Para saber mais: [https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Global_Objects/Generator](https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Global_Objects/Generator) [https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Global_Objects/Iterator](https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Global_Objects/Iterator) ### Referência JSDoc TsDocs Ao trabalhar com uma base de código JavaScript, é possível ajudar o TypeScript a inferir o tipo correto usando comentários JSDoc com anotações adicionais para fornecer informações de tipo. Exemplo: ```typescript /** * Computa a potência de um dado número * @constructor * @param {number} base – O valor da base da expressão * @param {number} exponent – O valor do expoente da expressão */ function power(base: number, exponent: number) { return Math.pow(base, exponent); } power(10, 2); // function power(base: number, exponent: number): number ``` A documentação completa é fornecida neste link: [https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html](https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html) A partir da versão 3.7, é possível gerar definições de tipo .d.ts a partir da sintaxe JSDoc do JavaScript. Mais informações podem ser encontradas aqui: [https://www.typescriptlang.org/docs/handbook/declaration-files/dts-from-js.html](https://www.typescriptlang.org/docs/handbook/declaration-files/dts-from-js.html) ### @types Pacotes sob a organização @types são convenções especiais de nomenclatura de pacotes usadas para fornecer definições de tipo para bibliotecas ou módulos JavaScript existentes. Por exemplo, usando: ```shell npm install --save-dev @types/lodash ``` Instalará as definições de tipo de `lodash` em seu projeto atual. Para contribuir com as definições de tipo do pacote @types, envie um pull request para [https://github.com/DefinitelyTyped/DefinitelyTyped](https://github.com/DefinitelyTyped/DefinitelyTyped). ### JSX JSX (JavaScript XML) é uma extensão da sintaxe da linguagem JavaScript que permite escrever código semelhante a HTML em seus arquivos JavaScript ou TypeScript. É comumente usado no React para definir a estrutura HTML. O TypeScript estende as capacidades do JSX fornecendo verificação de tipos e análise estática. Para usar JSX, você precisa definir a opção de compilador `jsx` em seu arquivo `tsconfig.json`. Duas opções de configuração comuns: * "preserve": emite arquivos .jsx com o JSX inalterado. Esta opção diz ao TypeScript para manter a sintaxe JSX como está e não transformá-la durante o processo de compilação. Você pode usar esta opção se tiver uma ferramenta separada, como o Babel, que lida com a transformação. * "react": habilita a transformação JSX integrada do TypeScript. `React.createElement` será usado. Todas as opções estão disponíveis aqui: [https://www.typescriptlang.org/tsconfig#jsx](https://www.typescriptlang.org/tsconfig#jsx) ### Módulos ES6 O TypeScript suporta ES6 (ECMAScript 2015) e muitas versões subsequentes. Isso significa que você pode usar a sintaxe ES6, como arrow functions, template literals, classes, módulos, desestruturação e muito mais. Para habilitar recursos do ES6 em seu projeto, você pode especificar a propriedade `target` no tsconfig.json. Um exemplo de configuração: ```json { "compilerOptions": { "target": "es6", "module": "es6", "moduleResolution": "node", "sourceMap": true, "outDir": "dist" }, "include": ["src"] } ``` ### Operador de Exponenciação ES7 O operador de exponenciação (`**`) calcula o valor obtido elevando o primeiro operando à potência do segundo operando. Ele funciona de forma semelhante a `Math.pow()`, mas com a capacidade adicional de aceitar BigInts como operandos. O TypeScript suporta totalmente este operador usando como `target` no seu arquivo tsconfig.json a versão `es2016` ou superior. ```typescript console.log(2 ** (2 ** 2)); // 16 ``` ### A Instrução for-await-of Este é um recurso do JavaScript totalmente suportado no TypeScript que permite iterar sobre objetos iteráveis assíncronos a partir da versão de destino es2018. ```typescript async function* asyncNumbers(): AsyncIterableIterator { yield Promise.resolve(1); yield Promise.resolve(2); yield Promise.resolve(3); } (async () => { for await (const num of asyncNumbers()) { console.log(num); } })(); ``` ### Nova meta-propriedade target Você pode usar no TypeScript a meta-propriedade `new.target` que permite determinar se uma função ou construtor foi invocado usando o operador `new`. Ela permite detectar se um objeto foi criado como resultado de uma chamada de construtor. ```typescript class Parent { constructor() { console.log(new.target); // Registra a função construtora usada para criar uma instância } } class Child extends Parent { constructor() { super(); } } const parentX = new Parent(); // [Function: Parent] const child = new Child(); // [Function: Child] ``` ### Expressões de Importação Dinâmica É possível carregar módulos condicionalmente ou carregá-los sob demanda (lazy load) usando a proposta do ECMAScript para importação dinâmica, que é suportada no TypeScript. A sintaxe para expressões de importação dinâmica no TypeScript é as seguinte: ```typescript async function renderWidget() { const container = document.getElementById('widget'); if (container !== null) { const widget = await import('./widget'); // Importação dinâmica widget.render(container); } } renderWidget(); ``` ### "tsc –watch" Este comando inicia o compilador TypeScript com o parâmetro `--watch`, com a capacidade de recompilar automaticamente os arquivos TypeScript sempre que forem modificados. ```shell tsc --watch ``` A partir da versão 4.9 do TypeScript, o monitoramento de arquivos depende principalmente de eventos do sistema de arquivos, recorrendo automaticamente à sondagem (polling) se um observador baseado em eventos não puder ser estabelecido. ### Operador de Asserção Não-Nulo (Non-null Assertion Operator) O Operador de Asserção Não-Nulo (Postfix !) também chamado de Asserções de Atribuição Definitiva (Definite Assignment Assertions) é um recurso do TypeScript que permite asseverar que uma variável ou propriedade não é nula ou indefinida, mesmo que a análise de tipo estática do TypeScript sugira que poderia ser. Com este recurso, é possível remover qualquer verificação explícita. ```typescript type Person = { name: string; }; const printName = (person?: Person) => { console.log(`O nome é ${person!.name}`); }; ``` ### Declarações com Valor Padrão (Defaulted declarations) Declarações com valor padrão são usadas quando uma variável ou parâmetro recebe um valor padrão. Isso significa que se nenhum valor for fornecido para essa variável ou parâmetro, o valor padrão será usado no lugar. ```typescript function greet(name: string = 'Anônimo'): void { console.log(`Olá, ${name}!`); } greet(); // Olá, Anônimo! greet('John'); // Olá, John! ``` ### Encadeamento Opcional (Optional Chaining) O operador de encadeamento opcional `?.` funciona como o operador de ponto regular (`.`) para acessar propriedades ou métodos. No entanto, ele trata graciosamente valores nulos ou indefinidos terminando a expressão e retornando `undefined`, em vez de lançar um erro. ```typescript type Person = { name: string; age?: number; address?: { street?: string; city?: string; }; }; const person: Person = { name: 'John', }; console.log(person.address?.city); // undefined ``` ### Operador de Coalescência Nula (Nullish coalescing operator) O operador de coalescência nula `??` retorna o valor do lado direito se o lado esquerdo for `null` ou `undefined`; caso contrário, retorna o valor do lado esquerdo. ```typescript const foo = null ?? 'foo'; console.log(foo); // foo const baz = 1 ?? 'baz'; const baz2 = 0 ?? 'baz'; console.log(baz); // 1 console.log(baz2); // 0 ``` ### Tipos de Literal de Template (Template Literal Types) Tipos de Literal de Template permitem manipular valores de string em nível de tipo e gerar novos tipos de string baseados em existentes. Eles são úteis para criar tipos mais expressivos e precisos a partir de operações baseadas em string. ```typescript type Department = 'engineering' | 'hr'; type Language = 'english' | 'spanish'; type Id = `${Department}-${Language}-id`; // "engineering-english-id" | "engineering-spanish-id" | "hr-english-id" | "hr-spanish-id" ``` ### Sobrecarga de Função (Function overloading) A sobrecarga de função permite definir múltiplas assinaturas de função para o mesmo nome de função, cada uma com diferentes tipos de parâmetros e tipo de retorno. Quando você chama uma função sobrecarregada, o TypeScript usa os argumentos fornecidos para determinar a assinatura de função correta: ```typescript function makeGreeting(name: string): string; function makeGreeting(names: string[]): string[]; function makeGreeting(person: unknown): unknown { if (typeof person === 'string') { return `Hi ${person}!`; } else if (Array.isArray(person)) { return person.map(name => `Hi, ${name}!`); } throw new Error('Não foi possível saudar'); } makeGreeting('Simon'); makeGreeting(['Simone', 'John']); ``` ### Tipos Recursivos Um Tipo Recursivo é um tipo que pode se referir a si mesmo. Isso é útil para definir estruturas de dados que possuem uma estrutura hierárquica ou recursiva (aninhamento potencialmente infinito), como listas ligadas, árvores e grafos. ```typescript type ListNode = { data: T; next: ListNode | undefined; }; ``` ### Tipos Condicionais Recursivos É possível definir relacionamentos de tipo complexos usando lógica e recursão no TypeScript. Vamos detalhar em termos simples: Tipos Condicionais: permite definir tipos baseados em condições booleanas: ```typescript type CheckNumber = T extends number ? 'Número' : 'Não é um número'; type A = CheckNumber<123>; // 'Número' type B = CheckNumber<'abc'>; // 'Não é um número' ``` Recursão: significa uma definição de tipo que se refere a si mesma dentro de sua própria definição: ```typescript type Json = string | number | boolean | null | Json[] | { [key: string]: Json }; const data: Json = { prop1: true, prop2: 'prop2', prop3: { prop4: [], }, }; ``` Tipos Condicionais Recursivos combinam lógica condicional e recursão. Isso significa que uma definição de tipo pode depender de si mesma através de lógica condicional, criando relacionamentos de tipo complexos e flexíveis. ```typescript type Flatten = T extends Array ? Flatten : T; type NestedArray = [1, [2, [3, 4], 5], 6]; type FlattenedArray = Flatten; // 2 | 3 | 4 | 5 | 1 | 6 ``` ### Suporte a Módulos ECMAScript no Node O Node.js adicionou suporte para Módulos ECMAScript a partir da versão 15.3.0, e o TypeScript tem suporte a Módulos ECMAScript para Node.js desde a versão 4.7. Este suporte pode ser habilitado usando a propriedade `module` com o valor `nodenext` no arquivo tsconfig.json. Aqui está um exemplo: ```json { "compilerOptions": { "module": "nodenext", "outDir": "./lib", "declaration": true } } ``` O Node.js suporta duas extensões de arquivo para módulos: `.mjs` para módulos ES e `.cjs` para módulos CommonJS. As extensões de arquivo equivalentes no TypeScript são `.mts` para módulos ES e `.cts` para módulos CommonJS. Quando o compilador TypeScript transpila esses arquivos para JavaScript, ele criará arquivos `.mjs` e `.cjs`. Se você quiser usar módulos ES em seu projeto, você pode definir a propriedade `type` como "module" em seu arquivo package.json. Isso instrui o Node.js a tratar o projeto como um projeto de módulo ES. Além disso, o TypeScript também suporta declarações de tipo em arquivos .d.ts. Esses arquivos de declaração fornecem informações de tipo para bibliotecas ou módulos escritos em TypeScript, permitindo que outros desenvolvedores os utilizem com a verificação de tipo e os recursos de preenchimento automático do TypeScript. ### Funções de Asserção (Assertion Functions) No TypeScript, funções de asserção são funções que indicam a verificação de uma condição específica com base em seu valor de retorno. Em sua forma mais simples, uma função assert examina um predicado fornecido e lança um erro quando o predicado é avaliado como falso. ```typescript function isNumber(value: unknown): asserts value is number { if (typeof value !== 'number') { throw new Error('Não é um número'); } } ``` Ou pode ser declarada como uma expressão de função: ```typescript type AssertIsNumber = (value: unknown) => asserts value is number; const isNumber: AssertIsNumber = value => { if (typeof value !== 'number') { throw new Error('Não é um número'); } }; ``` Funções de asserção compartilham semelhanças com guardas de tipo (type guards). As guardas de tipo foram inicialmente introduzidas para realizar verificações em tempo de execução e garantir o tipo de um valor dentro de um escopo específico. Especificamente, uma guarda de tipo é uma função que avalia um predicado de tipo e retorna um valor booleano indicando se the predicado é verdadeiro ou falso. Isso difere ligeiramente das funções de asserção, onde a intenção é lançar um erro em vez de retornar falso quando o predicado não for satisfeito. Exemplo de guarda de tipo: ```typescript const isNumber = (value: unknown): value is number => typeof value === 'number'; ``` ### Tipos de Tupla Variádicos (Variadic Tuple Types) Tipos de Tupla Variádicos são recursos introduzidos na versão 4.0 do TypeScript. Vamos começar revisando o que é uma tupla: Um tipo tupla é um array que possui um comprimento definido, e onde o tipo de cada elemento é conhecido: ```typescript type Student = [string, number]; const [name, age]: Student = ['Simone', 20]; ``` O termo "variádico" significa aridade indefinida (aceita um número variável de argumentos). Uma tupla variádica é um tipo tupla que possui todas as propriedades anteriores, mas o formato exato ainda não está definido: ```typescript type Bar = [boolean, ...T, number]; type A = Bar<[boolean]>; // [boolean, boolean, number] type B = Bar<['a', 'b']>; // [boolean, 'a', 'b', number] type C = Bar<[]>; // [boolean, number] ``` No código anterior, podemos ver que o formato da tupla é definido pelo genérico `T` passado. Tuplas variádicas podem aceitar múltiplos genéricos, tornando-as muito flexíveis: ```typescript type Bar = [...T, boolean, ...G]; type A = Bar<[number], [string]>; // [number, boolean, string] type B = Bar<['a', 'b'], [boolean]>; // ["a", "b", boolean, boolean] ``` Com as novas tuplas variádicas podemos usar: * Os espalhamentos (spreads) na sintaxe de tipo tupla agora podem ser genéricos, assim podemos representar operações de ordem superior em tuplas e arrays mesmo quando não conhecemos os tipos reais sobre os quais estamos operando. * Os elementos de resto (rest elements) podem ocorrer em qualquer lugar em uma tupla. Exemplo: ```typescript type Items = readonly unknown[]; function concat( arr1: T, arr2: U ): [...T, ...U] { return [...arr1, ...arr2]; } concat([1, 2, 3], ['4', '5', '6']); // [1, 2, 3, "4", "5", "6"] ``` ### Tipos Boxed (Boxed types) Tipos boxed referem-se aos objetos de empacotamento que são usados para representar tipos primitivos como objetos. Esses objetos de empacotamento fornecem funcionalidades e métodos adicionais que não estão disponíveis diretamente nos valores primitivos. Quando você acessa um método como `charAt` ou `normalize` em um primitivo `string`, o JavaScript o empacota em um objeto `String`, chama o método e depois descarta o objeto. Demonstração: ```typescript const originalNormalize = String.prototype.normalize; String.prototype.normalize = function () { console.log(this, typeof this); return originalNormalize.call(this); }; console.log('\u0041'.normalize()); ``` O TypeScript representa essa diferenciação fornecendo tipos separados para os primitivos e seus empacotadores de objeto correspondentes: * string => String * number => Number * boolean => Boolean * symbol => Symbol * bigint => BigInt Os tipos boxed geralmente não são necessários. Evite usar tipos boxed e, em vez disso, use o tipo para os primitivos, por exemplo, `string` em vez de `String`. ### Covariância e Contravariância no TypeScript Covariância e Contravariância são usadas para descrever como os relacionamentos funcionam ao lidar com herança ou atribuição de tipos. Covariância significa que um relacionamento de tipo preserva a direção da herança ou atribuição, então se um tipo A é um subtipo do tipo B, então um array do tipo A também é considerado um subtipo de um array do tipo B. O importante a notar aqui é que o relacionamento de subtipo é mantido; isso significa que a Covariância aceita subtipo mas não aceita supertipo. Contravariância significa que um relacionamento de tipo inverte a direção da herança ou atribuição, então se um tipo A é um subtipo do tipo B, então um array do tipo B é considerado um subtipo de um array do tipo A. O relacionamento de subtipo é invertido; isso significa que a Contravariância aceita supertipo mas não aceita subtipo. Notas: Bivariância significa aceitar tanto supertipo quanto subtipo. Exemplo: Digamos que tenhamos um espaço para todos os animais e um espaço separado apenas para cachorros. Na Covariância, você pode colocar todos os cachorros no espaço dos animais porque cachorros são um tipo de animal. Mas você não pode colocar todos os animais no espaço dos cachorros porque pode haver outros animais misturados. Na Contravariância, você não pode colocar todos os animais no espaço dos cachorros porque o espaço dos animais também pode conter outros animais. No entanto, você pode colocar todos os cachorros no espaço dos animais porque todos os cachorros também são animais. ```typescript // Exemplo de Covariância class Animal { name: string; constructor(name: string) { this.name = name; } } class Dog extends Animal { breed: string; constructor(name: string, breed: string) { super(name); this.breed = breed; } } let animals: Animal[] = []; let dogs: Dog[] = []; // Covariância permite atribuir array de subtipo (Dog) a array de supertipo (Animal) animals = dogs; dogs = animals; // Inválido: Tipo 'Animal[]' não é atribuível ao tipo 'Dog[]' // Exemplo de Contravariância type Feed = (animal: T) => void; let feedAnimal: Feed = (animal: Animal) => { console.log(`Nome do animal: ${animal.name}`); }; let feedDog: Feed = (dog: Dog) => { console.log(`Nome do cachorro: ${dog.name}, Raça: ${dog.breed}`); }; // Contravariância permite atribuir callback de supertipo (Animal) a callback de subtipo (Dog) feedDog = feedAnimal; feedAnimal = feedDog; // Inválido: Tipo 'Feed' não é atribuível ao tipo 'Feed'. ``` No TypeScript, relacionamentos de tipo para arrays são covariantes, enquanto relacionamentos de tipo para parâmetros de função são contravariantes. Isso significa que o TypeScript exibe tanto covariância quanto contravariância, dependendo do contexto. #### Anotações de Variância Opcionais para Parâmetros de Tipo A partir do TypeScript 4.7.0, podemos usar as palavras-chave `out` e `in` para sermos específicos sobre a anotação de variância. Para Covariante, use a palavra-chave `out`: ```typescript type AnimalCallback = () => T; // T é Covariante aqui ``` E para Contravariante, use a palavra-chave `in`: ```typescript type AnimalCallback = (value: T) => void; // T é Contravariante aqui ``` ### Assinaturas de Índice de Padrão de String de Template (Template String Pattern Index Signatures) Assinaturas de índice de padrão de string de template permitem definir assinaturas de índice flexíveis usando padrões de string de template. Este recurso nos permite criar objetos que podem ser indexados com padrões específicos de chaves de string, fornecendo mais controle e especificidade ao acessar e manipular propriedades. O TypeScript a partir da versão 4.4 permite assinaturas de índice para símbolos e padrões de string de template. ```typescript const uniqueSymbol = Symbol('description'); type MyKeys = `key-${string}`; type MyObject = { [uniqueSymbol]: string; [key: MyKeys]: number; }; const obj: MyObject = { [uniqueSymbol]: 'Chave de símbolo único', 'key-a': 123, 'key-b': 456, }; console.log(obj[uniqueSymbol]); // Chave de símbolo único console.log(obj['key-a']); // 123 console.log(obj['key-b']); // 456 ``` ### O Operador satisfies O `satisfies` permite verificar se um determinado tipo satisfaz uma interface ou condição específica. Em outras palavras, ele garante que um tipo possui todas as propriedades e métodos exigidos de uma interface específica. É uma maneira de garantir que uma variável se encaixe na definição de um tipo. Aqui está um exemplo: ```typescript type Columns = 'name' | 'nickName' | 'attributes'; type User = Record; // Anotação de Tipo usando `User` const user: User = { name: 'Simone', nickName: undefined, attributes: ['dev', 'admin'], }; // Nas linhas seguintes, o TypeScript não conseguirá inferir corretamente user.attributes?.map(console.log); // A propriedade 'map' não existe no tipo 'string | string[]'. A propriedade 'map' não existe no tipo 'string'. user.nickName; // string | string[] | undefined // Asserção de tipo usando `as` const user2 = { name: 'Simon', nickName: undefined, attributes: ['dev', 'admin'], } as User; // Aqui também, o TypeScript não conseguirá inferir corretamente user2.attributes?.map(console.log); // A propriedade 'map' não existe no tipo 'string | string[]'. A propriedade 'map' não existe no tipo 'string'. user2.nickName; // string | string[] | undefined // Usando operadores `satisfies` podemos inferir os tipos corretamente agora const user3 = { name: 'Simon', nickName: undefined, attributes: ['dev', 'admin'], } satisfies User; user3.attributes?.map(console.log); // TypeScript infere corretamente: string[] user3.nickName; // TypeScript infere corretamente: undefined ``` ### Importações e Exportações Apenas de Tipo (Type-Only Imports and Export) Importações e Exportações Apenas de Tipo permitem importar ou exportar tipos sem importar ou exportar os valores ou funções associados a esses tipos. Isso pode ser útil para reduzir o tamanho do seu bundle. Para usar importações apenas de tipo, você pode usar a palavra-chave `import type`. O TypeScript permite usar extensões de arquivo de declaração e implementação (.ts, .mts, .cts e .tsx) em importações apenas de tipo, independentemente das configurações de `allowImportingTsExtensions`. Por exemplo: ```typescript import type { House } from './house.ts'; ``` As seguintes são formas suportadas: ```typescript import type T from './mod'; import type { A, B } from './mod'; import type * as Types from './mod'; export type { T }; export type { T } from './mod'; ``` ### declaração using e Gerenciamento Explícito de Recursos (Explicit Resource Management) Uma declaração `using` é um vínculo imutável com escopo de bloco, semelhante ao `const`, usado para gerenciar recursos descartáveis (disposable). Quando inicializado com um valor, o método `Symbol.dispose` desse valor é registrado e subsequentemente executado ao sair do escopo de bloco envolvente. Isso é baseado no recurso de Gerenciamento de Recursos do ECMAScript, que é útil para realizar tarefas essenciais de limpeza após a criação do objeto, como fechar conexões, excluir arquivos e liberar memória. Notas: * Devido à sua introdução recente na versão 5.2 do TypeScript, a maioria dos ambientes de execução carece de suporte nativo. Você precisará de polyfills para: `Symbol.dispose`, `Symbol.asyncDispose`, `DisposableStack`, `AsyncDisposableStack`, `SuppressedError`. * Além disso, você precisará configurar seu tsconfig.json da seguinte forma: ```json { "compilerOptions": { "target": "es2022", "lib": ["es2022", "esnext.disposable", "dom"] } } ``` Exemplo: ```typescript //@ts-ignore Symbol.dispose ??= Symbol('Symbol.dispose'); // Polyfill simples const doWork = (): Disposable => { return { [Symbol.dispose]: () => { console.log('disposto (disposed)'); }, }; }; console.log(1); { using work = doWork(); // Recurso é declarado console.log(2); } // Recurso é descartado (ex: `work[Symbol.dispose]()` é avaliado) console.log(3); ``` O código registrará: ```shell 1 2 disposto (disposed) 3 ``` Um recurso elegível para descarte deve aderir à interface `Disposable`: ```typescript // lib.esnext.disposable.d.ts interface Disposable { [Symbol.dispose](): void; } ``` As declarações `using` registram as operações de descarte de recursos em uma pilha, garantindo que sejam descartadas na ordem inversa da declaração: ```typescript { using j = getA(), y = getB(); using k = getC(); } // descarta `C`, depois `B`, depois `A`. ``` Os recursos têm garantia de serem descartados, mesmo que ocorra código subsequente ou exceções. Isso pode levar o descarte a potencialmente lançar uma exceção, possivelmente suprimindo outra. Para manter informações sobre erros suprimidos, uma nova exceção nativa, `SuppressedError`, é introduzida. #### declaração await using Uma declaração `await using` lida com um recurso descartável de forma assíncrona. O valor deve ter um método `Symbol.asyncDispose`, que será aguardado ao final do bloco. ```typescript async function doWorkAsync() { await using work = doWorkAsync(); // Recurso é declarado } // Recurso é descartado (ex: `await work[Symbol.asyncDispose]()` é avaliado) ``` Para um recurso descartável de forma assíncrona, ele deve aderir à interface `Disposable` ou `AsyncDisposable`: ```typescript // lib.esnext.disposable.d.ts interface AsyncDisposable { [Symbol.asyncDispose](): Promise; } ``` ```typescript //@ts-ignore Symbol.asyncDispose ??= Symbol('Symbol.asyncDispose'); // Polyfill simples class DatabaseConnection implements AsyncDisposable { // Um método que é chamado quando o objeto é descartado assincronamente [Symbol.asyncDispose]() { // Fecha a conexão e retorna uma promise return this.close(); } async close() { console.log('Fechando a conexão...'); await new Promise(resolve => setTimeout(resolve, 1000)); console.log('Conexão fechada.'); } } async function doWork() { // Cria uma nova conexão e descarte-a assincronamente quando ela sair do escopo await using connection = new DatabaseConnection(); // Recurso é declarado console.log('Fazendo algum trabalho...'); } // Recurso é descartado (ex: `await connection[Symbol.asyncDispose]()` é avaliado) doWork(); ``` O código registra: ```shell Fazendo algum trabalho... Fechando a conexão... Conexão fechada. ``` As declarações `using` e `await using` são permitidas em Instruções: `for`, `for-in`, `for-of`, `for-await-of`, `switch`. ### Atributos de Importação (Import Attributes) Os Atributos de Importação do TypeScript 5.3 (rótulos para importações) dizem ao ambiente de execução como lidar com módulos (JSON, etc.). Isso melhora a segurança garantindo importações claras e se alinha com a Política de Segurança de Conteúdo (CSP) para carregamento de recursos mais seguro. O TypeScript garante que eles sejam válidos, mas deixa o ambiente de execução lidar com sua interpretação para manipulação específica de módulos. Exemplo: ```typescript import config from './config.json' with { type: 'json' }; ``` com importação dinâmica: ```typescript const config = import('./config.json', { with: { type: 'json' } }); ``` ================================================ FILE: README-sv_SE.md ================================================ # Den koncisa TypeScript-boken Den koncisa TypeScript-boken ger en omfattande och kortfattad översikt över TypeScripts kapaciteter. Den erbjuder tydliga förklaringar som täcker alla aspekter i den senaste versionen av språket, från dess kraftfulla typsystem till avancerade funktioner. Oavsett om du är nybörjare eller en erfaren utvecklare är denna bok en ovärderlig resurs för att förbättra din förståelse och skicklighet i TypeScript. Denna bok är helt gratis och öppen källkod. Jag tror att teknisk utbildning av hög kvalitet bör vara tillgänglig för alla, vilket är anledningen till att jag håller denna bok gratis och öppen. Om boken hjälpte dig att lösa en bugg, förstå ett knepigt koncept eller avancera i din karriär, vänligen överväg att stödja mitt arbete genom att betala vad du vill (föreslaget pris: 15 USD) eller sponsra en kaffe. Ditt stöd hjälper mig att hålla innehållet uppdaterat och utöka det med nya exempel och djupare förklaringar. [![Buy Me a Coffee](https://img.shields.io/badge/buy_me_a_coffee-FFDD00?style=for-the-badge&logo=buy-me-a-coffee&logoColor=black)](https://www.buymeacoffee.com/simonepoggiali) [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/donate/?business=QW82ZS956XLFY&no_recurring=0¤cy_code=EUR) ## Översättningar Denna bok har översatts till flera språkversioner, inklusive: [Kinesiska](https://github.com/gibbok/typescript-book/blob/main/README-zh_CN.md) [Italienska](https://github.com/gibbok/typescript-book/blob/main/README-it_IT.md) [Portugisiska (Brasilien)](https://github.com/gibbok/typescript-book/blob/main/README-pt_BR.md) [Svenska](https://github.com/gibbok/typescript-book/blob/main/README-sv_SE.md) ## Nedladdningar och webbplats Du kan också ladda ner Epub-versionen: [https://github.com/gibbok/typescript-book/tree/main/downloads](https://github.com/gibbok/typescript-book/tree/main/downloads) En onlineversion finns tillgänglig på: [https://gibbok.github.io/typescript-book](https://gibbok.github.io/typescript-book) ## Innehållsförteckning - [Den koncisa TypeScript-boken](#den-koncisa-typescript-boken) - [Översättningar](#översättningar) - [Nedladdningar och webbplats](#nedladdningar-och-webbplats) - [Innehållsförteckning](#innehållsförteckning) - [Introduktion](#introduktion) - [Om författaren](#om-författaren) - [Introduktion till TypeScript](#introduktion-till-typescript) - [Vad är TypeScript?](#vad-är-typescript) - [Varför TypeScript?](#varför-typescript) - [TypeScript och JavaScript](#typescript-och-javascript) - [TypeScript-kodgenerering](#typescript-kodgenerering) - [Modernt JavaScript nu (Downleveling)](#modernt-javascript-nu-downleveling) - [Komma igång med TypeScript](#komma-igång-med-typescript) - [Installation](#installation) - [Konfiguration](#konfiguration) - [TypeScript-konfigurationsfil](#typescript-konfigurationsfil) - [target](#target) - [lib](#lib) - [strict](#strict) - [module](#module) - [moduleResolution](#moduleresolution) - [esModuleInterop](#esmoduleinterop) - [jsx](#jsx) - [skipLibCheck](#skiplibcheck) - [files](#files) - [include](#include) - [exclude](#exclude) - [importHelpers](#importhelpers) - [Råd vid migrering till TypeScript](#råd-vid-migrering-till-typescript) - [Utforska typsystemet](#utforska-typsystemet) - [TypeScript-språktjänsten](#typescript-språktjänsten) - [Strukturell typning](#strukturell-typning) - [Grundläggande jämförelseregler i TypeScript](#grundläggande-jämförelseregler-i-typescript) - [Typer som mängder](#typer-som-mängder) - [Tilldela en typ: Typdeklarationer och typpåståenden](#tilldela-en-typ-typdeklarationer-och-typpåståenden) - [Typdeklaration](#typdeklaration) - [Typpåstående](#typpåstående) - [Omgivande deklarationer](#omgivande-deklarationer) - [Egenskapskontroll och kontroll av överskottsegenskaper](#egenskapskontroll-och-kontroll-av-överskottsegenskaper) - [Svaga typer](#svaga-typer) - [Strikt kontroll av objektliteraler (Freshness)](#strikt-kontroll-av-objektliteraler-freshness) - [Typinferens](#typinferens) - [Mer avancerade inferenser](#mer-avancerade-inferenser) - [Typbreddning](#typbreddning) - [Const](#const) - [Const-modifierare på typparametrar](#const-modifierare-på-typparametrar) - [Const-påstående](#const-påstående) - [Explicit typannotering](#explicit-typannotering) - [Typavsmalnande](#typavsmalnande) - [Villkor](#villkor) - [Kasta eller returnera](#kasta-eller-returnera) - [Diskriminerad union](#diskriminerad-union) - [Användardefinierade typvakter](#användardefinierade-typvakter) - [Primitiva typer](#primitiva-typer) - [string](#string) - [boolean](#boolean) - [number](#number) - [bigInt](#bigint) - [Symbol](#symbol) - [null och undefined](#null-och-undefined) - [Array](#array) - [any](#any) - [Typannoteringar](#typannoteringar) - [Valfria egenskaper](#valfria-egenskaper) - [Skrivskyddade egenskaper](#skrivskyddade-egenskaper) - [Indexsignaturer](#indexsignaturer) - [Utöka typer](#utöka-typer) - [Literaltyper](#literaltyper) - [Literalhärledning](#literalhärledning) - [strictNullChecks](#strictnullchecks) - [Enums](#enums) - [Numeriska enums](#numeriska-enums) - [Sträng-enums](#sträng-enums) - [Konstanta enums](#konstanta-enums) - [Omvänd mappning](#omvänd-mappning) - [Omgivande enums](#omgivande-enums) - [Beräknade och konstanta medlemmar](#beräknade-och-konstanta-medlemmar) - [Avsmalning](#avsmalning) - [typeof-typvakter](#typeof-typvakter) - [Sanningsvärdesavsmalning](#sanningsvärdesavsmalning) - [Likhetsavsmalning](#likhetsavsmalning) - [In-operatoravsmalning](#in-operatoravsmalning) - [instanceof-avsmalning](#instanceof-avsmalning) - [Tilldelningar](#tilldelningar) - [Kontrollflödesanalys](#kontrollflödesanalys) - [Typpredikat](#typpredikat) - [Diskriminerade unioner](#diskriminerade-unioner) - [Never-typen](#never-typen) - [Uttömmande kontroll](#uttömmande-kontroll) - [Objekttyper](#objekttyper) - [Tuppeltyp (Anonym)](#tuppeltyp-anonym) - [Namngiven tuppeltyp (Märkt)](#namngiven-tuppeltyp-märkt) - [Tuppel med fast längd](#tuppel-med-fast-längd) - [Unionstyp](#unionstyp) - [Intersektionstyper](#intersektionstyper) - [Typindexering](#typindexering) - [Typ från värde](#typ-från-värde) - [Typ från funktionsreturvärde](#typ-från-funktionsreturvärde) - [Typ från modul](#typ-från-modul) - [Mappade typer](#mappade-typer) - [Modifierare för mappade typer](#modifierare-för-mappade-typer) - [Villkorliga typer](#villkorliga-typer) - [Distributiva villkorliga typer](#distributiva-villkorliga-typer) - [infer-typinferens i villkorliga typer](#infer-typinferens-i-villkorliga-typer) - [Fördefinierade villkorliga typer](#fördefinierade-villkorliga-typer) - [Mall-unionstyper](#mall-unionstyper) - [Any-typen](#any-typen) - [Unknown-typen](#unknown-typen) - [Void-typen](#void-typen) - [Never-typen](#never-typen) - [Användning av Never-typen](#användning-av-never-typen) - [Interface och Type](#interface-och-type) - [Gemensam syntax](#gemensam-syntax) - [Grundläggande typer](#grundläggande-typer) - [Objekt och Interface](#objekt-och-interface) - [Union- och Intersection-typer](#union--och-intersection-typer) - [Inbyggda primitiva typer](#inbyggda-primitiva-typer) - [Vanliga inbyggda JS-objekt](#vanliga-inbyggda-js-objekt) - [Överlagringar](#överlagringar) - [Sammanslagning och utökning](#sammanslagning-och-utökning) - [Skillnader mellan Type och Interface](#skillnader-mellan-type-och-interface) - [Klass](#klass) - [Vanlig klasssyntax](#vanlig-klasssyntax) - [Konstruktor](#konstruktor) - [Privata och skyddade konstruktorer](#privata-och-skyddade-konstruktorer) - [Åtkomstmodifierare](#åtkomstmodifierare) - [Get och Set](#get-och-set) - [Auto-accessorer i klasser](#auto-accessorer-i-klasser) - [this](#this) - [Parameteregenskaper](#parameteregenskaper) - [Abstrakta klasser](#abstrakta-klasser) - [Med generics](#med-generics) - [Dekoratörer](#dekoratörer) - [Klassdekoratörer](#klassdekoratörer) - [Egenskapsdekoratör](#egenskapsdekoratör) - [Metoddekoratör](#metoddekoratör) - [Getter- och setter-dekoratörer](#getter--och-setter-dekoratörer) - [Dekoratörmetadata](#dekoratörmetadata) - [Arv](#arv) - [Statiska medlemmar](#statiska-medlemmar) - [Egenskapsinitiering](#egenskapsinitiering) - [Metodöverlagring](#metodöverlagring) - [Generics](#generics) - [Generisk typ](#generisk-typ) - [Generiska klasser](#generiska-klasser) - [Generiska begränsningar](#generiska-begränsningar) - [Generisk kontextuell avsmalning](#generisk-kontextuell-avsmalning) - [Raderade strukturella typer](#raderade-strukturella-typer) - [Namnrymder](#namnrymder) - [Symboler](#symboler) - [Trippelsnedstreck-direktiv](#trippelsnedstreck-direktiv) - [Typmanipulation](#typmanipulation) - [Skapa typer från typer](#skapa-typer-från-typer) - [Indexerade åtkomsttyper](#indexerade-åtkomsttyper) - [Verktygstyper](#verktygstyper) - [Awaited\](#awaitedt) - [Partial\](#partialt) - [Required\](#requiredt) - [Readonly\](#readonlyt) - [Record\](#recordk-t) - [Pick\](#pickt-k) - [Omit\](#omitt-k) - [Exclude\](#excludet-u) - [Extract\](#extractt-u) - [NonNullable\](#nonnullablet) - [Parameters\](#parameterst) - [ConstructorParameters\](#constructorparameterst) - [ReturnType\](#returntypet) - [InstanceType\](#instancetypet) - [ThisParameterType\](#thisparametertypet) - [OmitThisParameter\](#omitthisparametert) - [ThisType\](#thistypet) - [Uppercase\](#uppercaset) - [Lowercase\](#lowercaset) - [Capitalize\](#capitalizet) - [Uncapitalize\](#uncapitalizet) - [NoInfer\](#noinfert) - [Övrigt](#övrigt) - [Fel och undantagshantering](#fel-och-undantagshantering) - [Mixin-klasser](#mixin-klasser) - [Asynkrona språkfunktioner](#asynkrona-språkfunktioner) - [Iteratorer och generatorer](#iteratorer-och-generatorer) - [TsDocs JSDoc-referens](#tsdocs-jsdoc-referens) - [@types](#types) - [JSX](#jsx-1) - [ES6-moduler](#es6-moduler) - [ES7 exponentiationsoperator](#es7-exponentiationsoperator) - [for-await-of-satsen](#for-await-of-satsen) - [Metaegenskapen new.target](#metaegenskapen-newtarget) - [Dynamiska importuttryck](#dynamiska-importuttryck) - ["tsc –watch"](#tsc-watch) - [Non-null Assertion Operator](#non-null-assertion-operator) - [Standarddeklarationer](#standarddeklarationer) - [Valfri kedjning (Optional Chaining)](#valfri-kedjning-optional-chaining) - [Nullish coalescing-operatorn](#nullish-coalescing-operatorn) - [Mallsträngslitteraltyper (Template Literal Types)](#mallsträngslitteraltyper-template-literal-types) - [Funktionsöverlagring](#funktionsöverlagring) - [Rekursiva typer](#rekursiva-typer) - [Rekursiva villkorstyper](#rekursiva-villkorstyper) - [Stöd för ECMAScript-moduler i Node](#stöd-för-ecmascript-moduler-i-node) - [Assertionsfunktioner](#assertionsfunktioner) - [Variadiska tuppeltyper](#variadiska-tuppeltyper) - [Inkapslingstyper (Boxed types)](#inkapslingstyper-boxed-types) - [Kovarians och kontravarians i TypeScript](#kovarians-och-kontravarians-i-typescript) - [Valfria variansannotationer för typparametrar](#valfria-variansannotationer-för-typparametrar) - [Mallsträngsmönsterindexsignaturer](#mallsträngsmönsterindexsignaturer) - [satisfies-operatorn](#satisfies-operatorn) - [Importer och exporter av enbart typer](#importer-och-exporter-av-enbart-typer) - [using-deklaration och explicit resurshantering](#using-deklaration-och-explicit-resurshantering) - [await using-deklaration](#await-using-deklaration) - [Importattribut](#importattribut) ## Introduktion Välkommen till Den koncisa TypeScript-boken! Denna guide utrustar dig med väsentlig kunskap och praktiska färdigheter för effektiv TypeScript-utveckling. Upptäck nyckelkoncept och tekniker för att skriva ren, robust kod. Oavsett om du är nybörjare eller en erfaren utvecklare fungerar denna bok både som en omfattande guide och en praktisk referens för att utnyttja TypeScripts kraft i dina projekt. Denna bok täcker TypeScript 5.2. ## Om författaren Simone Poggiali är en erfaren Staff Engineer med en passion för att skriva professionell kod sedan 90-talet. Under sin internationella karriär har han bidragit till många projekt för ett brett spektrum av kunder, från startups till stora organisationer. Framstående företag som HelloFresh, Siemens, O2, Leroy Merlin och Snowplow har dragit nytta av hans expertis och engagemang. Du kan nå Simone Poggiali på följande plattformar: * LinkedIn: [https://www.linkedin.com/in/simone-poggiali](https://www.linkedin.com/in/simone-poggiali) * GitHub: [https://github.com/gibbok](https://github.com/gibbok) * X.com: [https://x.com/gibbok_coding](https://x.com/gibbok_coding) * E-post: gibbok.coding📧gmail.com Fullständig lista över bidragsgivare: [https://github.com/gibbok/typescript-book/graphs/contributors](https://github.com/gibbok/typescript-book/graphs/contributors) ## Introduktion till TypeScript ### Vad är TypeScript? TypeScript är ett starkt typat programmeringsspråk som bygger på JavaScript. Det designades ursprungligen av Anders Hejlsberg 2012 och utvecklas och underhålls för närvarande av Microsoft som ett öppen källkod-projekt. TypeScript kompileras till JavaScript och kan köras i vilken JavaScript-runtime som helst (t.ex. en webbläsare eller Node.js på en server). Det stöder flera programmeringsparadigm såsom funktionell, generisk, imperativ och objektorienterad programmering, och är ett kompilerat (transpilerat) språk som konverteras till JavaScript före exekvering. ### Varför TypeScript? TypeScript är ett starkt typat språk som hjälper till att förhindra vanliga programmeringsmisstag och undvika vissa typer av körtidsfel innan programmet körs. Ett starkt typat språk gör det möjligt för utvecklaren att specificera olika programbegränsningar och beteenden i datatypsdefinitionerna, vilket underlättar möjligheten att verifiera programvarans korrekthet och förhindra defekter. Detta är särskilt värdefullt i storskaliga applikationer. Några av fördelarna med TypeScript: * Statisk typning, valfritt starkt typad * Typinferens * Tillgång till ES6- och ES7-funktioner * Plattforms- och webbläsarkompatibilitet * Verktygsstöd med IntelliSense ### TypeScript och JavaScript TypeScript skrivs i `.ts`- eller `.tsx`-filer, medan JavaScript-filer skrivs i `.js`- eller `.jsx`-filer. Filer med tillägget `.tsx` eller `.jsx` kan innehålla JavaScript Syntax Extension JSX, som används i React för UI-utveckling. TypeScript är en typad supermängd av JavaScript (ECMAScript 2015) vad gäller syntax. All JavaScript-kod är giltig TypeScript-kod, men det omvända är inte alltid sant. Betrakta till exempel en funktion i en JavaScript-fil med tillägget `.js`, som följande: ```typescript const sum = (a, b) => a + b; ``` Funktionen kan konverteras och användas i TypeScript genom att ändra filtillägget till `.ts`. Men om samma funktion annoteras med TypeScript-typer kan den inte köras i någon JavaScript-runtime utan kompilering. Följande TypeScript-kod kommer att producera ett syntaxfel om den inte kompileras: ```typescript const sum = (a: number, b: number): number => a + b; ``` TypeScript designades för att upptäcka möjliga undantag som kan uppstå vid körning under kompileringstiden genom att låta utvecklaren definiera avsikten med typannoteringar. Dessutom kan TypeScript också fånga problem om ingen typannotering tillhandahålls. Till exempel specificerar följande kodavsnitt inga TypeScript-typer: ```typescript const items = [{ x: 1 }, { x: 2 }]; const result = items.filter(item => item.y); ``` I detta fall upptäcker TypeScript ett fel och rapporterar: ```text Property 'y' does not exist on type '{ x: number; }'. ``` TypeScripts typsystem är till stor del influerat av körtidsbeteendet hos JavaScript. Till exempel kan additionsoperatorn (+), som i JavaScript kan utföra antingen strängkonkatenering eller numerisk addition, modelleras på samma sätt i TypeScript: ```typescript const result = '1' + 1; // Result is of type string ``` Teamet bakom TypeScript har fattat ett medvetet beslut att flagga ovanlig användning av JavaScript som fel. Betrakta till exempel följande giltiga JavaScript-kod: ```typescript const result = 1 + true; // In JavaScript, the result is equal 2 ``` Dock kastar TypeScript ett fel: ```text Operator '+' cannot be applied to types 'number' and 'boolean'. ``` Detta fel uppstår eftersom TypeScript strikt upprätthåller typkompatibilitet, och i detta fall identifierar det en ogiltig operation mellan en number och en boolean. ### TypeScript-kodgenerering TypeScript-kompilatorn har två huvudansvar: kontrollera typfel och kompilera till JavaScript. Dessa två processer är oberoende av varandra. Typer påverkar inte kodens exekvering i en JavaScript-runtime, eftersom de raderas helt under kompileringen. TypeScript kan fortfarande producera JavaScript även vid typfel. Här är ett exempel på TypeScript-kod med ett typfel: ```typescript const add = (a: number, b: number): number => a + b; const result = add('x', 'y'); // Argument of type 'string' is not assignable to parameter of type 'number'. ``` Trots detta kan den fortfarande producera körbar JavaScript-utdata: ```typescript 'use strict'; const add = (a, b) => a + b; const result = add('x', 'y'); // xy ``` Det är inte möjligt att kontrollera TypeScript-typer vid körning. Till exempel: ```typescript interface Animal { name: string; } interface Dog extends Animal { bark: () => void; } interface Cat extends Animal { meow: () => void; } const makeNoise = (animal: Animal) => { if (animal instanceof Dog) { // 'Dog' only refers to a type, but is being used as a value here. // ... } }; ``` Eftersom typerna raderas efter kompilering finns det inget sätt att köra denna kod i JavaScript. För att känna igen typer vid körning behöver vi använda en annan mekanism. TypeScript erbjuder flera alternativ, där ett vanligt är "tagged union". Till exempel: ```typescript interface Dog { kind: 'dog'; // Tagged union bark: () => void; } interface Cat { kind: 'cat'; // Tagged union meow: () => void; } type Animal = Dog | Cat; const makeNoise = (animal: Animal) => { if (animal.kind === 'dog') { animal.bark(); } else { animal.meow(); } }; const dog: Dog = { kind: 'dog', bark: () => console.log('bark'), }; makeNoise(dog); ``` Egenskapen "kind" är ett värde som kan användas vid körning för att särskilja mellan objekt i JavaScript. Det är också möjligt att ett värde vid körning har en annan typ än den som deklarerades i typdeklarationen. Till exempel om utvecklaren har misstolkat en API-typ och annoterat den felaktigt. TypeScript är en supermängd av JavaScript, så nyckelordet "class" kan användas som en typ och ett värde vid körning. ```typescript class Animal { constructor(public name: string) {} } class Dog extends Animal { constructor( public name: string, public bark: () => void ) { super(name); } } class Cat extends Animal { constructor( public name: string, public meow: () => void ) { super(name); } } type Mammal = Dog | Cat; const makeNoise = (mammal: Mammal) => { if (mammal instanceof Dog) { mammal.bark(); } else { mammal.meow(); } }; const dog = new Dog('Fido', () => console.log('bark')); makeNoise(dog); ``` I JavaScript har en "class" en "prototype"-egenskap, och "instanceof"-operatorn kan användas för att testa om prototype-egenskapen för en konstruktor förekommer någonstans i prototypkedjan för ett objekt. TypeScript har ingen effekt på körtidsprestanda, eftersom alla typer raderas. Dock introducerar TypeScript viss overhead vid byggtid. ### Modernt JavaScript nu (Downleveling) TypeScript kan kompilera kod till vilken utgiven version av JavaScript som helst sedan ECMAScript 3 (1999). Detta innebär att TypeScript kan transpilera kod från de senaste JavaScript-funktionerna till äldre versioner, en process som kallas Downleveling. Detta möjliggör användning av modernt JavaScript samtidigt som maximal kompatibilitet med äldre körtidsmiljöer bibehålls. Det är viktigt att notera att vid transpilering till en äldre version av JavaScript kan TypeScript generera kod som kan medföra en prestandaoverhead jämfört med nativa implementeringar. Här är några av de moderna JavaScript-funktioner som kan användas i TypeScript: * ECMAScript-moduler istället för AMD-style "define"-callbacks eller CommonJS "require"-satser. * Klasser istället för prototyper. * Variabeldeklaration med "let" eller "const" istället för "var". * "for-of"-loop eller ".forEach" istället för den traditionella "for"-loopen. * Pilfunktioner istället för funktionsuttryck. * Destruktureringstilldelning. * Förkortade egenskaps-/metodnamn och beräknade egenskapsnamn. * Standardparametrar för funktioner. Genom att utnyttja dessa moderna JavaScript-funktioner kan utvecklare skriva mer uttrycksfull och koncis kod i TypeScript. ## Komma igång med TypeScript ### Installation Visual Studio Code erbjuder utmärkt stöd för TypeScript-språket men inkluderar inte TypeScript-kompilatorn. För att installera TypeScript-kompilatorn kan du använda en pakethanterare som npm eller yarn: ```shell npm install typescript --save-dev ``` eller ```shell yarn add typescript --dev ``` Se till att committa den genererade lockfilen för att säkerställa att varje teammedlem använder samma version av TypeScript. För att köra TypeScript-kompilatorn kan du använda följande kommandon ```shell npx tsc ``` eller ```shell yarn tsc ``` Det rekommenderas att installera TypeScript projektvis snarare än globalt, eftersom det ger en mer förutsägbar byggprocess. För enstaka tillfällen kan du dock använda följande kommando: ```shell npx tsc ``` eller installera det globalt: ```shell npm install -g typescript ``` Om du använder Microsoft Visual Studio kan du hämta TypeScript som ett paket i NuGet för dina MSBuild-projekt. I NuGet Package Manager Console kör du följande kommando: ```shell Install-Package Microsoft.TypeScript.MSBuild ``` Under TypeScript-installationen installeras två körbara filer: "tsc" som TypeScript-kompilatorn och "tsserver" som den fristående TypeScript-servern. Den fristående servern innehåller kompilatorn och språktjänster som kan användas av redigerare och IDE:er för att tillhandahålla intelligent kodkomplettering. Dessutom finns det flera TypeScript-kompatibla transpilerare tillgängliga, såsom Babel (via ett plugin) eller swc. Dessa transpilerare kan användas för att konvertera TypeScript-kod till andra målspråk eller versioner. ### Konfiguration TypeScript kan konfigureras med hjälp av tsc CLI-alternativ eller genom att använda en dedikerad konfigurationsfil kallad tsconfig.json som placeras i projektets rot. För att generera en tsconfig.json-fil förfylld med rekommenderade inställningar kan du använda följande kommando: ```shell tsc --init ``` När kommandot `tsc` körs lokalt kommer TypeScript att kompilera koden med den konfiguration som anges i den närmaste tsconfig.json-filen. Här är några exempel på CLI-kommandon som körs med standardinställningarna: ```shell tsc main.ts // Compile a specific file (main.ts) to JavaScript tsc src/*.ts // Compile any .ts files under the 'src' folder to JavaScript tsc app.ts util.ts --outfile index.js // Compile two TypeScript files (app.ts and util.ts) into a single JavaScript file (index.js) ``` ### TypeScript-konfigurationsfil En tsconfig.json-fil används för att konfigurera TypeScript-kompilatorn (tsc). Vanligtvis läggs den till i projektets rot, tillsammans med filen `package.json`. Observera: * tsconfig.json accepterar kommentarer även om det är i json-format. * Det är tillrådligt att använda denna konfigurationsfil istället för kommandoradsalternativ. På följande länk hittar du den fullständiga dokumentationen och dess schema: [https://www.typescriptlang.org/tsconfig](https://www.typescriptlang.org/tsconfig) [https://www.typescriptlang.org/tsconfig/](https://www.typescriptlang.org/tsconfig/) Följande representerar en lista över de vanligaste och mest användbara konfigurationerna: #### target Egenskapen "target" används för att ange vilken version av JavaScript ECMAScript-version din TypeScript ska generera/kompilera till. För moderna webbläsare är ES6 ett bra alternativ, för äldre webbläsare rekommenderas ES5. #### lib Egenskapen "lib" används för att ange vilka biblioteksfiler som ska inkluderas vid kompilering. TypeScript inkluderar automatiskt API:er för funktioner som anges i "target"-egenskapen, men det är möjligt att utelämna eller välja specifika bibliotek för särskilda behov. Till exempel, om du arbetar med ett serverprojekt kan du exkludera "DOM"-biblioteket, som bara är användbart i en webbläsarmiljö. #### strict Egenskapen "strict" möjliggör starkare garantier och förbättrar typsäkerheten. Det är tillrådligt att alltid inkludera denna egenskap i ditt projekts tsconfig.json-fil. Att aktivera egenskapen "strict" gör att TypeScript: * Genererar kod med "use strict" för varje källfil. * Beaktar "null" och "undefined" i typkontrollprocessen. * Inaktiverar användningen av typen "any" när inga typannoteringar finns. * Ger ett fel vid användning av "this"-uttrycket, som annars skulle innebära typen "any". #### module Egenskapen "module" anger det modulsystem som stöds för det kompilerade programmet. Vid körning används en modulladdare för att lokalisera och köra beroenden baserat på det angivna modulsystemet. De vanligaste modulladdarna som används i JavaScript är Node.js CommonJS för serversidans applikationer och RequireJS för AMD-moduler i webbläsarbaserade webbapplikationer. TypeScript kan generera kod för olika modulsystem, inklusive UMD, System, ESNext, ES2015/ES6 och ES2020. Observera: Modulsystemet bör väljas baserat på målmiljön och den modulladdningsmekanism som finns tillgänglig i den miljön. #### moduleResolution Egenskapen "moduleResolution" anger strategin för modulupplösning. Använd "node" för modern TypeScript-kod, strategin "classic" används bara för gamla versioner av TypeScript (före 1.6). #### esModuleInterop Egenskapen "esModuleInterop" gör det möjligt att importera standard från CommonJS-moduler som inte exporterade med "default"-egenskapen. Denna egenskap tillhandahåller en shim för att säkerställa kompatibilitet i den genererade JavaScript-koden. Efter att ha aktiverat detta alternativ kan vi använda `import MyLibrary from "my-library"` istället för `import * as MyLibrary from "my-library"`. #### jsx Egenskapen "jsx" gäller bara för .tsx-filer som används i ReactJS och styr hur JSX-konstruktioner kompileras till JavaScript. Ett vanligt alternativ är "preserve" som kompilerar till en .jsx-fil och behåller JSX oförändrat så att det kan skickas vidare till olika verktyg som Babel för ytterligare transformationer. #### skipLibCheck Egenskapen "skipLibCheck" hindrar TypeScript från att typkontrollera hela importerade tredjepartspaket. Denna egenskap minskar kompileringstiden för ett projekt. TypeScript kommer fortfarande att kontrollera din kod mot typdefinitionerna som tillhandahålls av dessa paket. #### files Egenskapen "files" anger för kompilatorn en lista med filer som alltid måste inkluderas i programmet. #### include Egenskapen "include" anger för kompilatorn en lista med filer som vi vill inkludera. Denna egenskap tillåter glob-liknande mönster, såsom "\*_" för valfri underkatalog, "_" för valfritt filnamn och "?" för valfria tecken. #### exclude Egenskapen "exclude" anger för kompilatorn en lista med filer som inte bör inkluderas i kompileringen. Detta kan inkludera filer som "node_modules" eller testfiler. Observera: tsconfig.json tillåter kommentarer. ### importHelpers TypeScript använder hjälpkod när det genererar kod för vissa avancerade eller nedåtkompatibla JavaScript-funktioner. Som standard dupliceras dessa hjälpfunktioner i filer som använder dem. Alternativet `importHelpers` importerar dessa hjälpfunktioner från modulen `tslib` istället, vilket gör JavaScript-utdata mer effektiv. ### Råd vid migrering till TypeScript För stora projekt rekommenderas en gradvis övergång där TypeScript- och JavaScript-kod initialt samexisterar. Bara små projekt kan migreras till TypeScript på en gång. Det första steget i denna övergång är att introducera TypeScript i byggkedjeprocessen. Detta kan göras genom att använda kompilatoralternativet "allowJs", som tillåter att .ts- och .tsx-filer samexisterar med befintliga JavaScript-filer. Eftersom TypeScript faller tillbaka till typen "any" för en variabel när det inte kan härleda typen från JavaScript-filer, rekommenderas det att inaktivera "noImplicitAny" i dina kompilatoralternativ i början av migreringen. Det andra steget är att säkerställa att dina JavaScript-tester fungerar tillsammans med TypeScript-filer så att du kan köra tester allt eftersom du konverterar varje modul. Om du använder Jest, överväg att använda `ts-jest`, som gör det möjligt att testa TypeScript-projekt med Jest. Det tredje steget är att inkludera typdeklarationer för tredjepartsbibliotek i ditt projekt. Dessa deklarationer kan hittas antingen medföljande eller på DefinitelyTyped. Du kan söka efter dem med [https://www.typescriptlang.org/dt/search](https://www.typescriptlang.org/dt/search) och installera dem med: ```shell npm install --save-dev @types/package-name ``` eller ```shell yarn add --dev @types/package-name ``` Det fjärde steget är att migrera modul för modul med en nedifrån-och-upp-metod, genom att följa ditt beroendeträd med start från löven. Tanken är att börja konvertera moduler som inte är beroende av andra moduler. För att visualisera beroendegrafer kan du använda verktyget "madge". Bra kandidatmoduler för dessa initiala konverteringar är hjälpfunktioner och kod relaterad till externa API:er eller specifikationer. Det är möjligt att automatiskt generera TypeScript-typdefinitioner från Swagger-kontrakt, GraphQL- eller JSON-scheman som kan inkluderas i ditt projekt. När det inte finns några specifikationer eller officiella scheman tillgängliga kan du generera typer från rådata, såsom JSON som returneras av en server. Det rekommenderas dock att generera typer från specifikationer istället för data för att undvika att missa specialfall. Under migreringen bör du avstå från kodrefaktorisering och fokusera enbart på att lägga till typer i dina moduler. Det femte steget är att aktivera "noImplicitAny", vilket kommer att se till att alla typer är kända och definierade, vilket ger en bättre TypeScript-upplevelse för ditt projekt. Under migreringen kan du använda direktivet `@ts-check`, som aktiverar TypeScript-typkontroll i en JavaScript-fil. Detta direktiv tillhandahåller en lös version av typkontroll och kan initialt användas för att identifiera problem i JavaScript-filer. När `@ts-check` inkluderas i en fil kommer TypeScript att försöka härleda definitioner med hjälp av kommentarer i JSDoc-stil. Överväg dock att använda JSDoc-annoteringar bara i ett mycket tidigt skede av migreringen. Överväg att behålla standardvärdet för `noEmitOnError` i din tsconfig.json som false. Detta gör att du kan generera JavaScript-källkod även om fel rapporteras. ## Utforska typsystemet ### TypeScript-språktjänsten TypeScript-språktjänsten, även känd som tsserver, erbjuder olika funktioner såsom felrapportering, diagnostik, kompilera-vid-sparning, namnbyte, gå till definition, kompletteringslistor, signaturhjälp och mer. Den används främst av integrerade utvecklingsmiljöer (IDE:er) för att ge IntelliSense-stöd. Den integreras sömlöst med Visual Studio Code och används av verktyg som Conquer of Completion (Coc). Utvecklare kan utnyttja ett dedikerat API och skapa sina egna anpassade språktjänstplugin för att förbättra TypeScript-redigeringsupplevelsen. Detta kan vara särskilt användbart för att implementera speciella linting-funktioner eller möjliggöra automatisk komplettering för ett anpassat mallspråk. Ett exempel på ett verkligt anpassat plugin är "typescript-styled-plugin", som tillhandahåller syntaxfelrapportering och IntelliSense-stöd för CSS-egenskaper i styled components. För mer information och snabbstartsguider kan du hänvisa till den officiella TypeScript-wikin på GitHub: [https://github.com/microsoft/TypeScript/wiki/](https://github.com/microsoft/TypeScript/wiki/) ### Strukturell typning TypeScript är baserat på ett strukturellt typsystem. Detta innebär att kompatibiliteten och ekvivalensen hos typer bestäms av typens faktiska struktur eller definition, snarare än dess namn eller plats för deklaration, som i nominativa typsystem som C# eller C. TypeScripts strukturella typsystem designades baserat på hur JavaScripts dynamiska duck typing-system fungerar vid körning. Följande exempel är giltig TypeScript-kod. Som du kan observera har "X" och "Y" samma medlem "a", även om de har olika deklarationsnamn. Typerna bestäms av deras strukturer, och i detta fall, eftersom strukturerna är desamma, är de kompatibla och giltiga. ```typescript type X = { a: string; }; type Y = { a: string; }; const x: X = { a: 'a' }; const y: Y = x; // Valid ``` ### Grundläggande jämförelseregler i TypeScript TypeScripts jämförelseprocess är rekursiv och utförs på typer som är nästlade på valfri nivå. En typ "X" är kompatibel med "Y" om "Y" har åtminstone samma medlemmar som "X". ```typescript type X = { a: string; }; const y = { a: 'A', b: 'B' }; // Valid, as it has at least the same members as X const r: X = y; ``` Funktionsparametrar jämförs efter typer, inte efter deras namn: ```typescript type X = (a: number) => void; type Y = (a: number) => void; let x: X = (j: number) => undefined; let y: Y = (k: number) => undefined; y = x; // Valid x = y; // Valid ``` Funktionens returtyper måste vara desamma: ```typescript type X = (a: number) => undefined; type Y = (a: number) => number; let x: X = (a: number) => undefined; let y: Y = (a: number) => 1; y = x; // Invalid x = y; // Invalid ``` Returtypen för en källfunktion måste vara en undertyp av returtypen för en målfunktion: ```typescript let x = () => ({ a: 'A' }); let y = () => ({ a: 'A', b: 'B' }); x = y; // Valid y = x; // Invalid member b is missing ``` Att utelämna funktionsparametrar är tillåtet, eftersom det är vanlig praxis i JavaScript, till exempel vid användning av "Array.prototype.map()": ```typescript [1, 2, 3].map((element, _index, _array) => element + 'x'); ``` Därför är följande typdeklarationer helt giltiga: ```typescript type X = (a: number) => undefined; type Y = (a: number, b: number) => undefined; let x: X = (a: number) => undefined; let y: Y = (a: number) => undefined; // Missing b parameter y = x; // Valid ``` Eventuella ytterligare valfria parametrar i källtypen är giltiga: ```typescript type X = (a: number, b?: number, c?: number) => undefined; type Y = (a: number) => undefined; let x: X = a => undefined; let y: Y = a => undefined; y = x; // Valid x = y; //Valid ``` Eventuella valfria parametrar i måltypen utan motsvarande parametrar i källtypen är giltiga och utgör inte ett fel: ```typescript type X = (a: number) => undefined; type Y = (a: number, b?: number) => undefined; let x: X = a => undefined; let y: Y = a => undefined; y = x; // Valid x = y; // Valid ``` Rest-parametern behandlas som en oändlig serie av valfria parametrar: ```typescript type X = (a: number, ...rest: number[]) => undefined; let x: X = a => undefined; //valid ``` Funktioner med överlagringar är giltiga om överlagringssignaturen är kompatibel med dess implementeringssignatur: ```typescript function x(a: string): void; function x(a: string, b: number): void; function x(a: string, b?: number): void { console.log(a, b); } x('a'); // Valid x('a', 1); // Valid function y(a: string): void; // Invalid, not compatible with implementation signature function y(a: string, b: number): void; function y(a: string, b: number): void { console.log(a, b); } y('a'); y('a', 1); ``` Jämförelse av funktionsparametrar lyckas om käll- och målparametrarna kan tilldelas supertyper eller undertyper (bivarians). ```typescript // Supertype class X { a: string; constructor(value: string) { this.a = value; } } // Subtype class Y extends X {} // Subtype class Z extends X {} type GetA = (x: X) => string; const getA: GetA = x => x.a; // Bivariance does accept supertypes console.log(getA(new X('x'))); // Valid console.log(getA(new Y('Y'))); // Valid console.log(getA(new Z('z'))); // Valid ``` Enums är jämförbara och giltiga med tal och vice versa, men att jämföra Enum-värden från olika Enum-typer är ogiltigt. ```typescript enum X { A, B, } enum Y { A, B, C, } const xa: number = X.A; // Valid const ya: Y = 0; // Valid X.A === Y.A; // Invalid ``` Instanser av en klass genomgår en kompatibilitetskontroll för sina privata och skyddade medlemmar: ```typescript class X { public a: string; constructor(value: string) { this.a = value; } } class Y { private a: string; constructor(value: string) { this.a = value; } } let x: X = new Y('y'); // Invalid ``` Jämförelsekontrollen tar inte hänsyn till den olika arvshierarkin, till exempel: ```typescript class X { public a: string; constructor(value: string) { this.a = value; } } class Y extends X { public a: string; constructor(value: string) { super(value); this.a = value; } } class Z { public a: string; constructor(value: string) { this.a = value; } } let x: X = new X('x'); let y: Y = new Y('y'); let z: Z = new Z('z'); x === y; // Valid x === z; // Valid even if z is from a different inheritance hierarchy ``` Generics jämförs med hjälp av deras strukturer baserat på den resulterande typen efter tillämpning av den generiska parametern. Bara slutresultatet jämförs som en icke-generisk typ. ```typescript interface X { a: T; } let x: X = { a: 1 }; let y: X = { a: 'a' }; x === y; // Invalid as the type argument is used in the final structure ``` ```typescript interface X {} const x: X = 1; const y: X = 'a'; x === y; // Valid as the type argument is not used in the final structure ``` När generics inte har sitt typargument specificerat behandlas alla ospecificerade argument som typer med "any": ```typescript type X = (x: T) => T; type Y = (y: K) => K; let x: X = x => x; let y: Y = y => y; x = y; // Valid ``` Kom ihåg: ```typescript let a: number = 1; let b: number = 2; a = b; // Valid, everything is assignable to itself let c: any; c = 1; // Valid, all types are assignable to any let d: unknown; d = 1; // Valid, all types are assignable to unknown let e: unknown; let e1: unknown = e; // Valid, unknown is only assignable to itself and any let e2: any = e; // Valid let e3: number = e; // Invalid let f: never; f = 1; // Invalid, nothing is assignable to never let g: void; let g1: any; g = 1; // Invalid, void is not assignable to or from anything expect any g = g1; // Valid ``` Observera att när "strictNullChecks" är aktiverat behandlas "null" och "undefined" på liknande sätt som "void"; annars liknar de "never". ### Typer som mängder I TypeScript är en typ en mängd av möjliga värden. Denna mängd kallas även typens domän. Varje värde av en typ kan ses som ett element i en mängd. En typ fastställer de begränsningar som varje element i mängden måste uppfylla för att betraktas som en medlem av den mängden. TypeScripts primära uppgift är att kontrollera och verifiera om en mängd är en delmängd av en annan. TypeScript stöder olika typer av mängder: | Mängdterm | TypeScript | Anteckningar | | ---------------------- | ------------------------------- | ------------------------------------------------------------------------------------------------------------------ | | Tom mängd | never | "never" innehåller ingenting förutom sig själv | | Enelement-mängd | undefined / null / literal type | | | Ändlig mängd | boolean / union | | | Oändlig mängd | string / number / object | | | Universell mängd | any / unknown | Varje element är medlem i "any" och varje mängd är en delmängd av den / "unknown" är en typsäker motsvarighet till "any" | Här är några exempel: | TypeScript | Mängdterm | Exempel | | --------------------- | ---------------------- | ------------------------------------------------------------------------------- | | never | ∅ (tom mängd) | const x: never = 'x'; // Error: Type 'string' is not assignable to type 'never' | | | | | Literal type | Enelement-mängd | type X = 'X'; | | | | type Y = 7; | | | | | Värde tilldelbart till T | Värde ∈ T (medlem av) | type XY = 'X' \| 'Y'; | | | | const x: XY = 'X'; | | | | | T1 tilldelbart till T2 | T1 ⊆ T2 (delmängd av) | type XY = 'X' \| 'Y'; | | | | const x: XY = 'X'; | | | | const j: XY = 'J'; // Type '"J"' is not assignable to type 'XY'. | | | | | | T1 extends T2 | T1 ⊆ T2 (delmängd av) | type X = 'X' extends string ? true : false; | | | | | T1 \| T2 | T1 ∪ T2 (union) | type XY = 'X' \| 'Y'; | | | | type JK = 1 \| 2; | | | | | T1 & T2 | T1 ∩ T2 (snitt) | type X = \{ a: string \} | | | | type Y = \{ b: string \} | | | | type XY = X & Y | | | | const x: XY = \{ a: 'a', b: 'b' \} | | | | | unknown | Universell mängd | const x: unknown = 1 | En union, (T1 | T2) skapar en bredare mängd (båda): ```typescript type X = { a: string; }; type Y = { b: string; }; type XY = X | Y; const r: XY = { a: 'a', b: 'x' }; // Valid ``` En intersektion, (T1 & T2) skapar en smalare mängd (endast delade): ```typescript type X = { a: string; }; type Y = { a: string; b: string; }; type XY = X & Y; const r: XY = { a: 'a' }; // Invalid const j: XY = { a: 'a', b: 'b' }; // Valid ``` Nyckelordet `extends` kan betraktas som "delmängd av" i detta sammanhang. Det sätter en begränsning för en typ. När extends används med en generisk typ, behandlas den generiska typen som en oändlig mängd och begränsas till en mer specifik typ. Observera att `extends` inte har något att göra med hierarki i OOP-bemärkelse (det finns inget sådant koncept i TypeScript). TypeScript arbetar med mängder och har ingen strikt hierarki. Faktum är att, som i exemplet nedan, två typer kan överlappa utan att någon av dem är en undertyp av den andra (TypeScript betraktar strukturen, formen på objekten). ```typescript interface X { a: string; } interface Y extends X { b: string; } interface Z extends Y { c: string; } const z: Z = { a: 'a', b: 'b', c: 'c' }; interface X1 { a: string; } interface Y1 { a: string; b: string; } interface Z1 { a: string; b: string; c: string; } const z1: Z1 = { a: 'a', b: 'b', c: 'c' }; const r: Z1 = z; // Valid ``` ### Tilldela en typ: Typdeklarationer och Typpåståenden En typ kan tilldelas på olika sätt i TypeScript: #### Typdeklaration I följande exempel använder vi x: X (": Type") för att deklarera en typ för variabeln x. ```typescript type X = { a: string; }; // Type declaration const x: X = { a: 'a', }; ``` Om variabeln inte har det angivna formatet kommer TypeScript att rapportera ett fel. Till exempel: ```typescript type X = { a: string; }; const x: X = { a: 'a', b: 'b', // Error: Object literal may only specify known properties }; ``` #### Typpåstående Det är möjligt att lägga till ett påstående genom att använda nyckelordet `as`. Detta talar om för kompilatorn att utvecklaren har mer information om en typ och tystar eventuella fel som kan uppstå. Till exempel: ```typescript type X = { a: string; }; const x = { a: 'a', b: 'b', } as X; ``` I exemplet ovan påstås objektet x ha typen X med hjälp av nyckelordet as. Detta informerar TypeScript-kompilatorn om att objektet överensstämmer med den angivna typen, även om det har en extra egenskap b som inte finns i typdefinitionen. Typpåståenden är användbara i situationer där en mer specifik typ behöver anges, särskilt vid arbete med DOM:en. Till exempel: ```typescript const myInput = document.getElementById('my_input') as HTMLInputElement; ``` Här används typpåståendet as HTMLInputElement för att tala om för TypeScript att resultatet av getElementById ska behandlas som ett HTMLInputElement. Typpåståenden kan också användas för att mappa om nycklar, som visas i exemplet nedan med malliteraler: ```typescript type J = { [Property in keyof Type as `prefix_${string & Property}`]: () => Type[Property]; }; type X = { a: string; b: number; }; type Y = J; ``` I detta exempel använder typen `J` en mappad typ med en malliteral för att mappa om nycklarna i Type. Den skapar nya egenskaper med ett "prefix_" tillagt till varje nyckel, och deras motsvarande värden är funktioner som returnerar de ursprungliga egenskapsvärdena. Det är värt att notera att när man använder ett typpåstående kommer TypeScript inte att utföra kontroll av överskottsegenskaper. Därför är det generellt att föredra att använda en typdeklaration när objektets struktur är känd i förväg. #### Omgivande deklarationer Omgivande deklarationer är filer som beskriver typer för JavaScript-kod. De har filnamnsformatet `.d.ts`. De importeras vanligtvis och används för att annotera befintliga JavaScript-bibliotek eller för att lägga till typer till befintliga JS-filer i ditt projekt. Många vanliga bibliotekstyper finns på: [https://github.com/DefinitelyTyped/DefinitelyTyped/](https://github.com/DefinitelyTyped/DefinitelyTyped/) och kan installeras med: ```shell npm install --save-dev @types/library-name ``` För dina egna omgivande deklarationer kan du importera dem med "triple-slash"-referensen: ```typescript /// ``` Du kan använda omgivande deklarationer även i JavaScript-filer med `// @ts-check`. Nyckelordet `declare` möjliggör typdefinitioner för befintlig JavaScript-kod utan att importera den, och fungerar som en platshållare för typer från en annan fil eller globalt. ### Egenskapskontroll och kontroll av överskottsegenskaper TypeScript bygger på ett strukturellt typsystem, men kontroll av överskottsegenskaper är en egenskap hos TypeScript som gör det möjligt att kontrollera om ett objekt har exakt de egenskaper som anges i typen. Kontroll av överskottsegenskaper utförs vid tilldelning av objektliteraler till variabler eller när de skickas som argument till funktionens överskottsegenskap, till exempel. ```typescript type X = { a: string; }; const y = { a: 'a', b: 'b' }; const x: X = y; // Valid because structural typing const w: X = { a: 'a', b: 'b' }; // Invalid because excess property checking ``` ### Svaga typer En typ anses vara svag när den inte innehåller annat än en uppsättning helt valfria egenskaper: ```typescript type X = { a?: string; b?: string; }; ``` TypeScript betraktar det som ett fel att tilldela något till en svag typ när det inte finns någon överlappning. Till exempel ger följande ett fel: ```typescript type Options = { a?: string; b?: string; }; const fn = (options: Options) => undefined; fn({ c: 'c' }); // Invalid ``` Även om det inte rekommenderas, är det möjligt att kringgå denna kontroll genom att använda typpåstående om det behövs: ```typescript type Options = { a?: string; b?: string; }; const fn = (options: Options) => undefined; fn({ c: 'c' } as Options); // Valid ``` Eller genom att lägga till `unknown` i indexsignaturen till den svaga typen: ```typescript type Options = { [prop: string]: unknown; a?: string; b?: string; }; const fn = (options: Options) => undefined; fn({ c: 'c' }); // Valid ``` ### Strikt kontroll av objektliteraler (Freshness) Strikt kontroll av objektliteraler, ibland kallad "freshness", är en funktion i TypeScript som hjälper till att fånga överskotts- eller felstavade egenskaper som annars skulle gå obemärkta vid normala strukturella typkontroller. När man skapar en objektliteral betraktar TypeScript-kompilatorn den som "fresh". Om objektliteralen tilldelas till en variabel eller skickas som parameter kommer TypeScript att ge ett fel om objektliteralen anger egenskaper som inte finns i måltypen. Dock försvinner "freshness" när en objektliteral breddas eller ett typpåstående används. Här är några exempel för att illustrera: ```typescript type X = { a: string }; type Y = { a: string; b: string }; let x: X; x = { a: 'a', b: 'b' }; // Freshness check: Invalid assignment var y: Y; y = { a: 'a', bx: 'bx' }; // Freshness check: Invalid assignment const fn = (x: X) => console.log(x.a); fn(x); fn(y); // Widening: No errors, structurally type compatible fn({ a: 'a', bx: 'b' }); // Freshness check: Invalid argument let c: X = { a: 'a' }; let d: Y = { a: 'a', b: '' }; c = d; // Widening: No Freshness check ``` ### Typinferens TypeScript kan härleda typer när ingen annotering tillhandahålls vid: * Variabelinitiering. * Medlemsinitiering. * Inställning av standardvärden för parametrar. * Funktionens returtyp. Till exempel: ```typescript let x = 'x'; // The type inferred is string ``` TypeScript-kompilatorn analyserar värdet eller uttrycket och bestämmer dess typ baserat på tillgänglig information. ### Mer avancerade inferenser När flera uttryck används vid typinferens letar TypeScript efter de "bästa gemensamma typerna". Till exempel: ```typescript let x = [1, 'x', 1, null]; // The type inferred is: (string | number | null)[] ``` Om kompilatorn inte kan hitta de bästa gemensamma typerna returnerar den en unionstyp. Till exempel: ```typescript let x = [new RegExp('x'), new Date()]; // Type inferred is: (RegExp | Date)[] ``` TypeScript använder "kontextuell typning" baserat på variabelns placering för att härleda typer. I följande exempel vet kompilatorn att `e` är av typen `MouseEvent` på grund av händelsetypen `click` som definieras i filen lib.d.ts, vilken innehåller omgivande deklarationer för olika vanliga JavaScript-konstruktioner och DOM:en: ```typescript window.addEventListener('click', function (e) {}); // The inferred type of e is MouseEvent ``` ### Typbreddning Typbreddning är den process där TypeScript tilldelar en typ till en variabel som initierats utan att en typannotering angavs. Den tillåter övergång från smal till bredare typ men inte tvärtom. I följande exempel: ```typescript let x = 'x'; // TypeScript infers as string, a wide type let y: 'y' | 'x' = 'y'; // y types is a union of literal types y = x; // Invalid Type 'string' is not assignable to type '"x" | "y"'. ``` TypeScript tilldelar `string` till `x` baserat på det enda värde som angavs vid initieringen (`x`), detta är ett exempel på breddning. TypeScript tillhandahåller sätt att kontrollera breddningsprocessen, till exempel genom att använda "const". ### Const Att använda nyckelordet `const` vid deklaration av en variabel resulterar i en smalare typinferens i TypeScript. Till exempel: ```typescript const x = 'x'; // TypeScript infers the type of x as 'x', a narrower type let y: 'y' | 'x' = 'y'; y = x; // Valid: The type of x is inferred as 'x' ``` Genom att använda `const` för att deklarera variabeln x, smalnas dess typ av till det specifika literalvärdet 'x'. Eftersom typen av x är avsmalnad kan den tilldelas till variabeln y utan något fel. Anledningen till att typen kan härledas är att `const`-variabler inte kan omtilldelas, så deras typ kan smalnas av till en specifik literaltyp, i detta fall literaltypen 'x'. #### Const-modifierare på typparametrar Från version 5.0 av TypeScript är det möjligt att ange attributet `const` på en generisk typparameter. Detta möjliggör härledning av den mest precisa typen möjligt. Låt oss se ett exempel utan att använda `const`: ```typescript function identity(value: T) { // No const here return value; } const values = identity({ a: 'a', b: 'b' }); // Type infered is: { a: string; b: string; } ``` Som du kan se härleddes egenskaperna `a` och `b` med typen `string`. Låt oss nu se skillnaden med `const`-versionen: ```typescript function identity(value: T) { // Using const modifier on type parameters return value; } const values = identity({ a: 'a', b: 'b' }); // Type infered is: { a: "a"; b: "b"; } ``` Nu kan vi se att egenskaperna `a` och `b` härleddes som `const`, så `a` och `b` behandlas som strängliteraler snarare än bara `string`-typer. #### Const-påstående Denna funktion låter dig deklarera en variabel med en mer precis literaltyp baserat på dess initieringsvärde, och signalerar till kompilatorn att värdet ska behandlas som en oföränderlig literal. Här är några exempel: På en enskild egenskap: ```typescript const v = { x: 3 as const, }; v.x = 3; ``` På ett helt objekt: ```typescript const v = { x: 1, y: 2, } as const; ``` Detta kan vara särskilt användbart vid definition av typen för en tupel: ```typescript const x = [1, 2, 3]; // number[] const y = [1, 2, 3] as const; // Tuple of readonly [1, 2, 3] ``` ### Explicit typannotering Vi kan vara specifika och ange en typ. I följande exempel är egenskapen `x` av typen `number`: ```typescript const v = { x: 1, // Inferred type: number (widening) }; v.x = 3; // Valid ``` Vi kan göra typannoteringen mer specifik genom att använda en union av literaltyper: ```typescript const v: { x: 1 | 2 | 3 } = { x: 1, // x is now a union of literal types: 1 | 2 | 3 }; v.x = 3; // Valid v.x = 100; // Invalid ``` ### Typavsmalnande Typavsmalnande är den process i TypeScript där en generell typ smalnas av till en mer specifik typ. Detta sker när TypeScript analyserar koden och avgör att vissa villkor eller operationer kan förfina typinformationen. Avsmalnande av typer kan ske på olika sätt, bland annat: #### Villkor Genom att använda villkorssatser, som `if` eller `switch`, kan TypeScript smalna av typen baserat på utfallet av villkoret. Till exempel: ```typescript let x: number | undefined = 10; if (x !== undefined) { x += 100; // The type is number, which had been narrowed by the condition } ``` #### Kasta eller returnera Att kasta ett fel eller returnera tidigt från en gren kan användas för att hjälpa TypeScript smalna av en typ. Till exempel: ```typescript let x: number | undefined = 10; if (x === undefined) { throw 'error'; } x += 100; ``` Andra sätt att smalna av typer i TypeScript inkluderar: * `instanceof`-operatorn: Används för att kontrollera om ett objekt är en instans av en specifik klass. * `in`-operatorn: Används för att kontrollera om en egenskap finns i ett objekt. * `typeof`-operatorn: Används för att kontrollera typen av ett värde vid körning. * Inbyggda funktioner som `Array.isArray()`: Används för att kontrollera om ett värde är en array. #### Diskriminerad union Att använda en "diskriminerad union" är ett mönster i TypeScript där en explicit "tagg" läggs till objekt för att skilja mellan olika typer inom en union. Detta mönster kallas också en "taggad union". I följande exempel representeras "taggen" av egenskapen "type": ```typescript type A = { type: 'type_a'; value: number }; type B = { type: 'type_b'; value: string }; const x = (input: A | B): string | number => { switch (input.type) { case 'type_a': return input.value + 100; // type is A case 'type_b': return input.value + 'extra'; // type is B } }; ``` #### Användardefinierade typvakter I fall där TypeScript inte kan avgöra en typ är det möjligt att skriva en hjälpfunktion känd som en "användardefinierad typvakt". I följande exempel kommer vi att använda ett typpredikat för att smalna av typen efter att viss filtrering har tillämpats: ```typescript const data = ['a', null, 'c', 'd', null, 'f']; const r1 = data.filter(x => x != null); // The type is (string | null)[], TypeScript was not able to infer the type properly const isValid = (item: string | null): item is string => item !== null; // Custom type guard const r2 = data.filter(isValid); // The type is fine now string[], by using the predicate type guard we were able to narrow the type ``` ## Primitiva typer TypeScript stöder 7 primitiva typer. En primitiv datatyp avser en typ som inte är ett objekt och som inte har några metoder kopplade till sig. I TypeScript är alla primitiva typer oföränderliga, vilket innebär att deras värden inte kan ändras efter att de har tilldelats. ### string Den primitiva typen `string` lagrar textdata, och värdet omges alltid av dubbla eller enkla citattecken. ```typescript const x: string = 'x'; const y: string = 'y'; ``` Strängar kan sträcka sig över flera rader om de omges av backtick-tecknet (`): ```typescript let sentence: string = `xxx, yyy`; ``` ### boolean Datatypen `boolean` i TypeScript lagrar ett binärt värde, antingen `true` eller `false`. ```typescript const isReady: boolean = true; ``` ### number En `number`-datatyp i TypeScript representeras med ett 64-bitars flyttalsvärde. En `number`-typ kan representera heltal och bråktal. TypeScript stöder även hexadecimala, binära och oktala tal, till exempel: ```typescript const decimal: number = 10; const hexadecimal: number = 0xa00d; // Hexadecimal starts with 0x const binary: number = 0b1010; // Binary starts with 0b const octal: number = 0o633; // Octal starts with 0o ``` ### bigInt En `bigInt` representerar numeriska värden som är mycket stora (253 – 1) och inte kan representeras med en `number`. En `bigInt` kan skapas genom att anropa den inbyggda funktionen `BigInt()` eller genom att lägga till `n` i slutet av ett heltalsliteral: ```typescript const x: bigint = BigInt(9007199254740991); const y: bigint = 9007199254740991n; ``` Noteringar: * `bigInt`-värden kan inte blandas med `number` och kan inte användas med inbyggd `Math`, de måste konverteras till samma typ. * `bigInt`-värden är bara tillgängliga om målkonfigurationen är ES2020 eller högre. ### Symbol Symboler är unika identifierare som kan användas som egenskapsnycklar i objekt för att förhindra namnkonflikter. ```typescript type Obj = { [sym: symbol]: number; }; const a = Symbol('a'); const b = Symbol('b'); let obj: Obj = {}; obj[a] = 123; obj[b] = 456; console.log(obj[a]); // 123 console.log(obj[b]); // 456 ``` ### null och undefined `null` och `undefined`-typerna representerar båda inget värde eller frånvaron av något värde. Typen `undefined` betyder att värdet inte är tilldelat eller initierat och indikerar en oavsiktlig frånvaro av värde. Typen `null` betyder att vi vet att fältet inte har något värde, alltså är värdet otillgängligt, och det indikerar en avsiktlig frånvaro av värde. ### Array En `array` är en datatyp som kan lagra flera värden av samma typ eller inte. Den kan definieras med följande syntax: ```typescript const x: string[] = ['a', 'b']; const y: Array = ['a', 'b']; const j: Array = ['a', 1, 'b', 2]; // Union ``` TypeScript stöder skrivskyddade arrayer med följande syntax: ```typescript const x: readonly string[] = ['a', 'b']; // Readonly modifier const y: ReadonlyArray = ['a', 'b']; const j: ReadonlyArray = ['a', 1, 'b', 2]; j.push('x'); // Invalid ``` TypeScript stöder tupler och skrivskyddade tupler: ```typescript const x: [string, number] = ['a', 1]; const y: readonly [string, number] = ['a', 1]; ``` ### any Datatypen `any` representerar bokstavligen "vilket som helst" värde, det är standardvärdet när TypeScript inte kan härleda typen eller den inte är specificerad. När `any` används hoppar TypeScript-kompilatorn över typkontrollen, så det finns ingen typsäkerhet när `any` används. Använd i allmänhet inte `any` för att tysta kompilatorn när ett fel uppstår, fokusera istället på att åtgärda felet eftersom det med `any` är möjligt att bryta kontrakt och vi förlorar fördelarna med TypeScripts autokomplettering. Typen `any` kan vara användbar vid en gradvis migrering från JavaScript till TypeScript, eftersom den kan tysta kompilatorn. För nya projekt, använd TypeScript-konfigurationen `noImplicitAny` som gör att TypeScript ger fel där `any` används eller härleds. Typen `any` är vanligtvis en källa till fel som kan dölja verkliga problem med dina typer. Undvik att använda den så mycket som möjligt. ## Typannoteringar På variabler som deklareras med `var`, `let` och `const` är det möjligt att valfritt lägga till en typ: ```typescript const x: number = 1; ``` TypeScript är bra på att härleda typer, särskilt enkla sådana, så dessa deklarationer är i de flesta fall inte nödvändiga. På funktioner är det möjligt att lägga till typannoteringar på parametrar: ```typescript function sum(a: number, b: number) { return a + b; } ``` Följande är ett exempel med en anonym funktion (så kallad lambda-funktion): ```typescript const sum = (a: number, b: number) => a + b; ``` Dessa annoteringar kan undvikas när ett standardvärde för en parameter finns: ```typescript const sum = (a = 10, b: number) => a + b; ``` Returtypannoteringar kan läggas till på funktioner: ```typescript const sum = (a = 10, b: number): number => a + b; ``` Detta är särskilt användbart för mer komplexa funktioner eftersom det att explicit skriva returtypen före en implementation kan hjälpa till att bättre tänka igenom funktionen. Generellt sett, överväg att annotera typsignaturer men inte lokala variabler i funktionskroppen, och lägg alltid till typer på objektliteraler. ## Valfria egenskaper Ett objekt kan specificera valfria egenskaper genom att lägga till ett frågetecken `?` i slutet av egenskapsnamnet: ```typescript type X = { a: number; b?: number; // Optional }; ``` Det är möjligt att ange ett standardvärde när en egenskap är valfri: ```typescript type X = { a: number; b?: number; }; const x = ({ a, b = 100 }: X) => a + b; ``` ## Skrivskyddade egenskaper Det är möjligt att förhindra skrivning till en egenskap genom att använda modifieraren `readonly` som ser till att egenskapen inte kan skrivas om, men den ger ingen garanti för total oföränderlighet: ```typescript interface Y { readonly a: number; } type X = { readonly a: number; }; type J = Readonly<{ a: number; }>; type K = { readonly [index: number]: string; }; ``` ## Indexsignaturer I TypeScript kan vi använda `string`, `number` och `symbol` som indexsignatur: ```typescript type K = { [name: string | number]: string; }; const k: K = { x: 'x', 1: 'b' }; console.log(k['x']); console.log(k[1]); console.log(k['1']); // Same result as k[1] ``` Observera att JavaScript automatiskt konverterar ett index med `number` till ett index med `string`, så `k[1]` eller `k["1"]` returnerar samma värde. ## Utöka typer Det är möjligt att utöka ett `interface` (kopiera medlemmar från en annan typ): ```typescript interface X { a: string; } interface Y extends X { b: string; } ``` Det är också möjligt att utöka från flera typer: ```typescript interface A { a: string; } interface B { b: string; } interface Y extends A, B { y: string; } ``` Nyckelordet `extends` fungerar bara på interface och klasser, för typer använd en intersektion: ```typescript type A = { a: number; }; type B = { b: number; }; type C = A & B; ``` Det är möjligt att utöka en typ med hjälp av en härledning men inte tvärtom: ```typescript type A = { a: string; }; interface B extends A { b: string; } ``` ## Literaltyper En literaltyp är en enskild elementuppsättning från en kollektiv typ, den definierar ett mycket exakt värde som är en JavaScript-primitiv. Literaltyper i TypeScript är tal, strängar och booleaner. Exempel på literaler: ```typescript const a = 'a'; // String literal type const b = 1; // Numeric literal type const c = true; // Boolean literal type ``` Sträng-, numeriska och booleska literaltyper används i unioner, typvakter och typalias. I följande exempel kan du se ett unionstypealias. `O` består bara av de angivna värdena, ingen annan sträng är giltig: ```typescript type O = 'a' | 'b' | 'c'; ``` ## Literalhärledning Literalhärledning är en funktion i TypeScript som gör att typen av en variabel eller parameter kan härledas baserat på dess värde. I följande exempel kan vi se att TypeScript betraktar `x` som en literaltyp eftersom värdet inte kan ändras senare, medan `y` härleds som sträng eftersom det kan ändras när som helst. ```typescript const x = 'x'; // Literal type of 'x', because this value cannot be changed let y = 'y'; // Type string, as we can change this value ``` I följande exempel kan vi se att `o.x` härleds som en `string` (och inte en literal av `a`) eftersom TypeScript anser att värdet kan ändras när som helst. ```typescript type X = 'a' | 'b'; let o = { x: 'a', // This is a wider string }; const fn = (x: X) => `${x}-foo`; console.log(fn(o.x)); // Argument of type 'string' is not assignable to parameter of type 'X' ``` Som du kan se ger koden ett fel när `o.x` skickas till `fn` eftersom X är en smalare typ. Vi kan lösa detta problem genom att använda typbekräftelse med `const` eller typen `X`: ```typescript let o = { x: 'a' as const, }; ``` eller: ```typescript let o = { x: 'a' as X, }; ``` ## strictNullChecks `strictNullChecks` är ett TypeScript-kompilatoralternativ som framtvingar strikt null-kontroll. När detta alternativ är aktiverat kan variabler och parametrar bara tilldelas `null` eller `undefined` om de uttryckligen har deklarerats att vara av den typen med hjälp av unionstypen `null` | `undefined`. Om en variabel eller parameter inte uttryckligen deklareras som nullable kommer TypeScript att generera ett fel för att förhindra potentiella körtidsfel. ## Enums I TypeScript är en `enum` en uppsättning namngivna konstantvärden. ```typescript enum Color { Red = '#ff0000', Green = '#00ff00', Blue = '#0000ff', } ``` Enums kan definieras på olika sätt: ### Numeriska enums I TypeScript är en numerisk Enum en Enum där varje konstant tilldelas ett numeriskt värde, med start från 0 som standard. ```typescript enum Size { Small, // value starts from 0 Medium, Large, } ``` Det är möjligt att ange anpassade värden genom att explicit tilldela dem: ```typescript enum Size { Small = 10, Medium, Large, } console.log(Size.Medium); // 11 ``` ### Sträng-enums I TypeScript är en sträng-Enum en Enum där varje konstant tilldelas ett strängvärde. ```typescript enum Language { English = 'EN', Spanish = 'ES', } ``` Notera: TypeScript tillåter användning av heterogena Enums där sträng- och numeriska medlemmar kan samexistera. ### Konstanta enums En konstant enum i TypeScript är en speciell typ av Enum där alla värden är kända vid kompileringstid och infogas överallt där enum:en används, vilket resulterar i mer effektiv kod. ```typescript const enum Language { English = 'EN', Spanish = 'ES', } console.log(Language.English); ``` Kommer att kompileras till: ```typescript console.log('EN' /* Language.English */); ``` Noteringar: Konstanta Enums har hårdkodade värden som raderar Enum:en, vilket kan vara mer effektivt i fristående bibliotek men är i allmänhet inte önskvärt. Dessutom kan konstanta enums inte ha beräknade medlemmar. ### Omvänd mappning I TypeScript avser omvänd mappning i Enums möjligheten att hämta Enum-medlemmens namn från dess värde. Som standard har Enum-medlemmar framåtmappningar från namn till värde, men omvända mappningar kan skapas genom att explicit ange värden för varje medlem. Omvända mappningar är användbara när du behöver slå upp en Enum-medlem efter dess värde, eller när du behöver iterera över alla Enum-medlemmar. Observera att bara numeriska Enum-medlemmar genererar omvända mappningar, medan sträng-Enum-medlemmar inte genererar någon omvänd mappning alls. Följande enum: ```typescript enum Grade { A = 90, B = 80, C = 70, F = 'fail', } ``` Kompileras till: ```javascript 'use strict'; var Grade; (function (Grade) { Grade[(Grade['A'] = 90)] = 'A'; Grade[(Grade['B'] = 80)] = 'B'; Grade[(Grade['C'] = 70)] = 'C'; Grade['F'] = 'fail'; })(Grade || (Grade = {})); ``` Därför fungerar mappning av värden till nycklar för numeriska enum-medlemmar, men inte för sträng-enum-medlemmar: ```typescript enum Grade { A = 90, B = 80, C = 70, F = 'fail', } const myGrade = Grade.A; console.log(Grade[myGrade]); // A console.log(Grade[90]); // A const failGrade = Grade.F; console.log(failGrade); // fail console.log(Grade[failGrade]); // Element implicitly has an 'any' type because index expression is not of type 'number'. ``` ### Omgivande enums En omgivande enum i TypeScript är en typ av Enum som definieras i en deklarationsfil (*.d.ts) utan en associerad implementation. Den låter dig definiera en uppsättning namngivna konstanter som kan användas på ett typsäkert sätt över olika filer utan att behöva importera implementationsdetaljerna i varje fil. ### Beräknade och konstanta medlemmar I TypeScript är en beräknad medlem en medlem av en Enum som har ett värde som beräknas vid körning, medan en konstant medlem är en medlem vars värde sätts vid kompileringstid och inte kan ändras under körning. Beräknade medlemmar är tillåtna i vanliga Enums, medan konstanta medlemmar är tillåtna i både vanliga och const enums. ```typescript // Constant members enum Color { Red = 1, Green = 5, Blue = Red + Green, } console.log(Color.Blue); // 6 generation at compilation time ``` ```typescript // Computed members enum Color { Red = 1, Green = Math.pow(2, 2), Blue = Math.floor(Math.random() * 3) + 1, } console.log(Color.Blue); // random number generated at run time ``` Enums betecknas av unioner som består av deras medlemstyper. Värdena för varje medlem kan bestämmas genom konstanta eller icke-konstanta uttryck, där medlemmar med konstanta värden tilldelas literaltyper. För att illustrera, betrakta deklarationen av typ E och dess undertyper E.A, E.B och E.C. I detta fall representerar E unionen E.A | E.B | E.C. ```typescript const identity = (value: number) => value; enum E { A = 2 * 5, // Numeric literal B = 'bar', // String literal C = identity(42), // Opaque computed } console.log(E.C); //42 ``` ## Avsmalning TypeScript-avsmalning är processen att förfina typen av en variabel inom ett villkorsblock. Detta är användbart när man arbetar med unionstyper, där en variabel kan ha mer än en typ. TypeScript känner igen flera sätt att avsmalna typen: ### typeof-typvakter typeof-typvakten är en specifik typvakt i TypeScript som kontrollerar typen av en variabel baserat på dess inbyggda JavaScript-typ. ```typescript const fn = (x: number | string) => { if (typeof x === 'number') { return x + 1; // x is number } return -1; }; ``` ### Sanningsvärdesavsmalning Sanningsvärdesavsmalning i TypeScript fungerar genom att kontrollera om en variabel är sann eller falsk för att avsmalna dess typ därefter. ```typescript const toUpperCase = (name: string | null) => { if (name) { return name.toUpperCase(); } else { return null; } }; ``` ### Likhetsavsmalning Likhetsavsmalning i TypeScript fungerar genom att kontrollera om en variabel är lika med ett specifikt värde eller inte, för att avsmalma dess typ därefter. Den används tillsammans med `switch`-satser och likhetsoperatorer som `===`, `!==`, `==` och `!=` för att avsmalma typer. ```typescript const checkStatus = (status: 'success' | 'error') => { switch (status) { case 'success': return true; case 'error': return null; } }; ``` ### In-operatoravsmalning `in`-operatoravsmalning i TypeScript är ett sätt att avsmalma typen av en variabel baserat på om en egenskap finns inom variabelns typ. ```typescript type Dog = { name: string; breed: string; }; type Cat = { name: string; likesCream: boolean; }; const getAnimalType = (pet: Dog | Cat) => { if ('breed' in pet) { return 'dog'; } else { return 'cat'; } }; ``` ### instanceof-avsmalning `instanceof`-operatoravsmalning i TypeScript är ett sätt att avsmalma typen av en variabel baserat på dess konstruktorfunktion, genom att kontrollera om ett objekt är en instans av en viss klass eller gränssnitt. ```typescript class Square { constructor(public width: number) {} } class Rectangle { constructor( public width: number, public height: number ) {} } function area(shape: Square | Rectangle) { if (shape instanceof Square) { return shape.width * shape.width; } else { return shape.width * shape.height; } } const square = new Square(5); const rectangle = new Rectangle(5, 10); console.log(area(square)); // 25 console.log(area(rectangle)); // 50 ``` ## Tilldelningar TypeScript-avsmalning med hjälp av tilldelningar är ett sätt att avsmalma typen av en variabel baserat på det tilldelade värdet. När en variabel tilldelas ett värde härleder TypeScript dess typ baserat på det tilldelade värdet, och avsmalmar variabelns typ för att matcha den härledda typen. ```typescript let value: string | number; value = 'hello'; if (typeof value === 'string') { console.log(value.toUpperCase()); } value = 42; if (typeof value === 'number') { console.log(value.toFixed(2)); } ``` ## Kontrollflödesanalys Kontrollflödesanalys i TypeScript är ett sätt att statiskt analysera kodflödet för att härleda typer av variabler, vilket gör det möjligt för kompilatorn att avsmalma typerna av dessa variabler efter behov, baserat på resultaten av analysen. Före TypeScript 4.4 tillämpades kodflödesanalys enbart på kod inom en if-sats, men från och med TypeScript 4.4 kan den även tillämpas på villkorliga uttryck och diskriminantegenskapsåtkomster som indirekt refereras genom const-variabler. Till exempel: ```typescript const f1 = (x: unknown) => { const isString = typeof x === 'string'; if (isString) { x.length; } }; const f2 = ( obj: { kind: 'foo'; foo: string } | { kind: 'bar'; bar: number } ) => { const isFoo = obj.kind === 'foo'; if (isFoo) { obj.foo; } else { obj.bar; } }; ``` Några exempel där avsmalning inte sker: ```typescript const f1 = (x: unknown) => { let isString = typeof x === 'string'; if (isString) { x.length; // Error, no narrowing because isString it is not const } }; const f6 = ( obj: { kind: 'foo'; foo: string } | { kind: 'bar'; bar: number } ) => { const isFoo = obj.kind === 'foo'; obj = obj; if (isFoo) { obj.foo; // Error, no narrowing because obj is assigned in function body } }; ``` Observera: Upp till fem nivåer av indirektion analyseras i villkorliga uttryck. ## Typpredikat Typpredikat i TypeScript är funktioner som returnerar ett booleskt värde och används för att avsmalma typen av en variabel till en mer specifik typ. ```typescript const isString = (value: unknown): value is string => typeof value === 'string'; const foo = (bar: unknown) => { if (isString(bar)) { console.log(bar.toUpperCase()); } else { console.log('not a string'); } }; ``` ## Diskriminerade unioner Diskriminerade unioner i TypeScript är en typ av unionstyp som använder en gemensam egenskap, känd som diskriminanten, för att avsmalma uppsättningen av möjliga typer för unionen. ```typescript type Square = { kind: 'square'; // Discriminant size: number; }; type Circle = { kind: 'circle'; // Discriminant radius: number; }; type Shape = Square | Circle; const area = (shape: Shape) => { switch (shape.kind) { case 'square': return Math.pow(shape.size, 2); case 'circle': return Math.PI * Math.pow(shape.radius, 2); } }; const square: Square = { kind: 'square', size: 5 }; const circle: Circle = { kind: 'circle', radius: 2 }; console.log(area(square)); // 25 console.log(area(circle)); // 12.566370614359172 ``` ## Never-typen När en variabel avsmalnas till en typ som inte kan innehålla några värden, kommer TypeScript-kompilatorn att härleda att variabeln måste vara av typen `never`. Detta beror på att never-typen representerar ett värde som aldrig kan produceras. ```typescript const printValue = (val: string | number) => { if (typeof val === 'string') { console.log(val.toUpperCase()); } else if (typeof val === 'number') { console.log(val.toFixed(2)); } else { // val has type never here because it can never be anything other than a string or a number const neverVal: never = val; console.log(`Unexpected value: ${neverVal}`); } }; ``` ## Uttömmande kontroll Uttömmande kontroll är en funktion i TypeScript som säkerställer att alla möjliga fall av en diskriminerad union hanteras i en `switch`-sats eller en `if`-sats. ```typescript type Direction = 'up' | 'down'; const move = (direction: Direction) => { switch (direction) { case 'up': console.log('Moving up'); break; case 'down': console.log('Moving down'); break; default: const exhaustiveCheck: never = direction; console.log(exhaustiveCheck); // This line will never be executed } }; ``` `never`-typen används för att säkerställa att default-fallet är uttömmande och att TypeScript kommer att ge ett fel om ett nytt värde läggs till i Direction-typen utan att det hanteras i switch-satsen. ## Objekttyper I TypeScript beskriver objekttyper formen på ett objekt. De specificerar namnen och typerna på objektets egenskaper, samt huruvida dessa egenskaper är obligatoriska eller valfria. I TypeScript kan du definiera objekttyper på två primära sätt: Interface som definierar formen på ett objekt genom att specificera namnen, typerna och valfrihet hos dess egenskaper. ```typescript interface User { name: string; age: number; email?: string; } ``` Typalias, liknande ett interface, definierar formen på ett objekt. Det kan dock även skapa en ny anpassad typ som baseras på en befintlig typ eller en kombination av befintliga typer. Detta inkluderar att definiera unionstyper, intersektionstyper och andra komplexa typer. ```typescript type Point = { x: number; y: number; }; ``` Det är också möjligt att definiera en typ anonymt: ```typescript const sum = (x: { a: number; b: number }) => x.a + x.b; console.log(sum({ a: 5, b: 1 })); ``` ## Tuppeltyp (Anonym) En tuppeltyp är en typ som representerar en array med ett fast antal element och deras motsvarande typer. En tuppeltyp tvingar fram ett specifikt antal element och deras respektive typer i en fast ordning. Tuppeltyper är användbara när du vill representera en samling värden med specifika typer, där positionen för varje element i arrayen har en specifik betydelse. ```typescript type Point = [number, number]; ``` ## Namngiven tuppeltyp (Märkt) Tuppeltyper kan inkludera valfria etiketter eller namn för varje element. Dessa etiketter är till för läsbarhet och verktygsstöd, och påverkar inte de operationer du kan utföra med dem. ```typescript type T = string; type Tuple1 = [T, T]; type Tuple2 = [a: T, b: T]; type Tuple3 = [a: T, T]; // Named Tuple plus Anonymous Tuple ``` ## Tuppel med fast längd En tuppel med fast längd är en specifik typ av tuppel som tvingar fram ett fast antal element av specifika typer, och tillåter inte några ändringar av tuppelns längd efter att den har definierats. Tupplar med fast längd är användbara när du behöver representera en samling värden med ett specifikt antal element och specifika typer, och du vill säkerställa att längden och typerna av tuppeln inte kan ändras oavsiktligt. ```typescript const x = [10, 'hello'] as const; x.push(2); // Error ``` ## Unionstyp En unionstyp är en typ som representerar ett värde som kan vara en av flera typer. Unionstyper betecknas med symbolen `|` mellan varje möjlig typ. ```typescript let x: string | number; x = 'hello'; // Valid x = 123; // Valid ``` ## Intersektionstyper En intersektionstyp är en typ som representerar ett värde som har alla egenskaper hos två eller flera typer. Intersektionstyper betecknas med symbolen `&` mellan varje typ. ```typescript type X = { a: string; }; type Y = { b: string; }; type J = X & Y; // Intersection const j: J = { a: 'a', b: 'b', }; ``` ## Typindexering Typindexering avser möjligheten att definiera typer som kan indexeras med en nyckel som inte är känd i förväg, genom att använda en indexsignatur för att specificera typen för egenskaper som inte uttryckligen deklarerats. ```typescript type Dictionary = { [key: string]: T; }; const myDict: Dictionary = { a: 'a', b: 'b' }; console.log(myDict['a']); // Returns a ``` ## Typ från värde Typ från värde i TypeScript avser den automatiska härledningen av en typ från ett värde eller uttryck genom typinferens. ```typescript const x = 'x'; // TypeScript infers 'x' as a string literal with 'const' (immutable), but widens it to 'string' with 'let' (reassignable). ``` ## Typ från funktionsreturvärde Typ från funktionsreturvärde avser möjligheten att automatiskt härleda returtypen av en funktion baserat på dess implementation. Detta gör att TypeScript kan bestämma typen av det värde som returneras av funktionen utan explicita typannoteringar. ```typescript const add = (x: number, y: number) => x + y; // TypeScript can infer that the return type of the function is a number ``` ## Typ från modul Typ från modul avser möjligheten att använda en moduls exporterade värden för att automatiskt härleda deras typer. När en modul exporterar ett värde med en specifik typ kan TypeScript använda den informationen för att automatiskt härleda typen av det värdet när det importeras till en annan modul. ```typescript // calc.ts export const add = (x: number, y: number) => x + y; // index.ts import { add } from 'calc'; const r = add(1, 2); // r is number ``` ## Mappade typer Mappade typer i TypeScript låter dig skapa nya typer baserade på en befintlig typ genom att transformera varje egenskap med hjälp av en mappningsfunktion. Genom att mappa befintliga typer kan du skapa nya typer som representerar samma information i ett annat format. För att skapa en mappad typ kommer du åt egenskaperna hos en befintlig typ med `keyof`-operatorn och ändrar dem sedan för att producera en ny typ. I följande exempel: ```typescript type MyMappedType = { [P in keyof T]: T[P][]; }; type MyType = { foo: string; bar: number; }; type MyNewType = MyMappedType; const x: MyNewType = { foo: ['hello', 'world'], bar: [1, 2, 3], }; ``` definierar vi MyMappedType för att mappa över T:s egenskaper och skapa en ny typ där varje egenskap är en array av sin ursprungliga typ. Med hjälp av detta skapar vi MyNewType för att representera samma information som MyType, men med varje egenskap som en array. ## Modifierare för mappade typer Modifierare för mappade typer i TypeScript möjliggör transformation av egenskaper inom en befintlig typ: * `readonly` eller `+readonly`: Detta gör en egenskap i den mappade typen skrivskyddad. * `-readonly`: Detta gör att en egenskap i den mappade typen kan ändras. * `?`: Detta gör en egenskap i den mappade typen valfri. Exempel: ```typescript type ReadOnly = { readonly [P in keyof T]: T[P] }; // All properties marked as read-only type Mutable = { -readonly [P in keyof T]: T[P] }; // All properties marked as mutable type MyPartial = { [P in keyof T]?: T[P] }; // All properties marked as optional ``` ## Villkorliga typer Villkorliga typer är ett sätt att skapa en typ som beror på ett villkor, där typen som ska skapas bestäms baserat på resultatet av villkoret. De definieras med nyckelordet `extends` och en ternär operator för att villkorligt välja mellan två typer. ```typescript type IsArray = T extends any[] ? true : false; const myArray = [1, 2, 3]; const myNumber = 42; type IsMyArrayAnArray = IsArray; // Type true type IsMyNumberAnArray = IsArray; // Type false ``` ## Distributiva villkorliga typer Distributiva villkorliga typer är en funktion som gör det möjligt att distribuera en typ över en union av typer, genom att tillämpa en transformation på varje medlem av unionen individuellt. Detta kan vara särskilt användbart vid arbete med mappade typer eller typer av högre ordning. ```typescript type Nullable = T extends any ? T | null : never; type NumberOrBool = number | boolean; type NullableNumberOrBool = Nullable; // number | boolean | null ``` ## infer-typinferens i villkorliga typer Nyckelordet `infer` används i villkorliga typer för att härleda (extrahera) typen av en generisk parameter från en typ som beror på den. Detta gör att du kan skriva mer flexibla och återanvändbara typdefinitioner. ```typescript type ElementType = T extends (infer U)[] ? U : never; type Numbers = ElementType; // number type Strings = ElementType; // string ``` ## Fördefinierade villkorliga typer I TypeScript är fördefinierade villkorliga typer inbyggda villkorliga typer som tillhandahålls av språket. De är utformade för att utföra vanliga typtransformationer baserade på egenskaperna hos en given typ. `Exclude`: Denna typ tar bort alla typer från Type som kan tilldelas till ExcludedType. `Extract`: Denna typ extraherar alla typer från Union som kan tilldelas till Type. `NonNullable`: Denna typ tar bort null och undefined från Type. `ReturnType`: Denna typ extraherar returtypen av en funktionstyp Type. `Parameters`: Denna typ extraherar parametertyperna av en funktionstyp Type. `Required`: Denna typ gör alla egenskaper i Type obligatoriska. `Partial`: Denna typ gör alla egenskaper i Type valfria. `Readonly`: Denna typ gör alla egenskaper i Type skrivskyddade. ## Mall-unionstyper Mall-unionstyper kan användas för att slå samman och manipulera text inuti typsystemet, till exempel: ```typescript type Status = 'active' | 'inactive'; type Products = 'p1' | 'p2'; type ProductId = `id-${Products}-${Status}`; // "id-p1-active" | "id-p1-inactive" | "id-p2-active" | "id-p2-inactive" ``` ## Any-typen `any`-typen är en speciell typ (universell supertyp) som kan användas för att representera vilken typ av värde som helst (primitiver, objekt, arrayer, funktioner, fel, symboler). Den används ofta i situationer där typen av ett värde inte är känd vid kompilering, eller vid arbete med värden från externa API:er eller bibliotek som inte har TypeScript-typningar. Genom att använda `any`-typen indikerar du för TypeScript-kompilatorn att värden ska representeras utan några begränsningar. För att maximera typsäkerheten i din kod, överväg följande: * Begränsa användningen av `any` till specifika fall där typen verkligen är okänd. * Returnera inte `any`-typer från en funktion, eftersom detta försvagar typsäkerheten i kod som använder den. * Istället för `any`, använd `@ts-ignore` om du behöver tysta kompilatorn. ```typescript let value: any; value = true; // Valid value = 7; // Valid ``` ## Unknown-typen I TypeScript representerar `unknown`-typen ett värde av en okänd typ. Till skillnad från `any`-typen, som tillåter vilken typ av värde som helst, kräver `unknown` en typkontroll eller assertion innan den kan användas på ett specifikt sätt, så inga operationer är tillåtna på en `unknown` utan att först göra en assertion eller avsmalning till en mer specifik typ. `unknown`-typen kan bara tilldelas till any-typen och `unknown`-typen själv, den är ett typsäkert alternativ till `any`. ```typescript let value: unknown; let value1: unknown = value; // Valid let value2: any = value; // Valid let value3: boolean = value; // Invalid let value4: number = value; // Invalid ``` ```typescript const add = (a: unknown, b: unknown): number | undefined => typeof a === 'number' && typeof b === 'number' ? a + b : undefined; console.log(add(1, 2)); // 3 console.log(add('x', 2)); // undefined ``` ## Void-typen Typen `void` används för att indikera att en funktion inte returnerar något värde. ```typescript const sayHello = (): void => { console.log('Hello!'); }; ``` ## Användning av Never-typen Typen `never` representerar värden som aldrig förekommer. Den används för att beteckna funktioner eller uttryck som aldrig returnerar eller kastar ett fel. Till exempel en oändlig loop: ```typescript const infiniteLoop = (): never => { while (true) { // do something } }; ``` Kasta ett fel: ```typescript const throwError = (message: string): never => { throw new Error(message); }; ``` Typen `never` är användbar för att säkerställa typsäkerhet och fånga potentiella fel i din kod. Den hjälper TypeScript att analysera och härleda mer precisa typer när den används i kombination med andra typer och kontrollflödessatser, till exempel: ```typescript type Direction = 'up' | 'down'; const move = (direction: Direction): void => { switch (direction) { case 'up': // move up break; case 'down': // move down break; default: const exhaustiveCheck: never = direction; throw new Error(`Unhandled direction: ${exhaustiveCheck}`); } }; ``` ## Interface och Type ### Gemensam syntax I TypeScript definierar interface strukturen hos objekt och specificerar namnen och typerna på egenskaper eller metoder som ett objekt måste ha. Den gemensamma syntaxen för att definiera ett interface i TypeScript är följande: ```typescript interface InterfaceName { property1: Type1; // ... method1(arg1: ArgType1, arg2: ArgType2): ReturnType; // ... } ``` På liknande sätt för typdefinition: ```typescript type TypeName = { property1: Type1; // ... method1(arg1: ArgType1, arg2: ArgType2): ReturnType; // ... }; ``` `interface InterfaceName` eller `type TypeName`: Definierar namnet på interfacet. `property1`: `Type1`: Specificerar interfacets egenskaper tillsammans med deras motsvarande typer. Flera egenskaper kan definieras, var och en separerad med ett semikolon. `method1(arg1: ArgType1, arg2: ArgType2): ReturnType;`: Specificerar interfacets metoder. Metoder definieras med sina namn, följt av en parameterlista inom parenteser och returtypen. Flera metoder kan definieras, var och en separerad med ett semikolon. Exempel på interface: ```typescript interface Person { name: string; age: number; greet(): void; } ``` Exempel på type: ```typescript type TypeName = { property1: string; method1(arg1: string, arg2: string): string; }; ``` I TypeScript används typer för att definiera formen på data och upprätthålla typkontroll. Det finns flera vanliga syntaxer för att definiera typer i TypeScript, beroende på det specifika användningsfallet. Här är några exempel: ### Grundläggande typer ```typescript let myNumber: number = 123; // number type let myBoolean: boolean = true; // boolean type let myArray: string[] = ['a', 'b']; // array of strings let myTuple: [string, number] = ['a', 123]; // tuple ``` ### Objekt och Interface ```typescript const x: { name: string; age: number } = { name: 'Simon', age: 7 }; ``` ### Union- och Intersection-typer ```typescript type MyType = string | number; // Union type let myUnion: MyType = 'hello'; // Can be a string myUnion = 123; // Or a number type TypeA = { name: string }; type TypeB = { age: number }; type CombinedType = TypeA & TypeB; // Intersection type let myCombined: CombinedType = { name: 'John', age: 25 }; // Object with both name and age properties ``` ## Inbyggda primitiva typer TypeScript har flera inbyggda primitiva typer som kan användas för att definiera variabler, funktionsparametrar och returtyper: * `number`: Representerar numeriska värden, inklusive heltal och flyttal. * `string`: Representerar textdata. * `boolean`: Representerar logiska värden, som kan vara antingen true eller false. * `null`: Representerar frånvaron av ett värde. * `undefined`: Representerar ett värde som inte har tilldelats eller inte har definierats. * `symbol`: Representerar en unik identifierare. Symboler används vanligtvis som nycklar för objektegenskaper. * `bigint`: Representerar heltal med godtycklig precision. * `any`: Representerar en dynamisk eller okänd typ. Variabler av typen any kan innehålla värden av vilken typ som helst, och de kringgår typkontroll. * `void`: Representerar frånvaron av någon typ. Den används vanligtvis som returtyp för funktioner som inte returnerar ett värde. * `never`: Representerar en typ för värden som aldrig förekommer. Den används vanligtvis som returtyp för funktioner som kastar ett fel eller går in i en oändlig loop. ## Vanliga inbyggda JS-objekt TypeScript är en utökning av JavaScript och inkluderar alla vanligt använda inbyggda JavaScript-objekt. Du kan hitta en utförlig lista över dessa objekt på Mozilla Developer Networks (MDN) dokumentationswebbplats: [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects) Här är en lista över några vanligt använda inbyggda JavaScript-objekt: * Function * Object * Boolean * Error * Number * BigInt * Math * Date * String * RegExp * Array * Map * Set * Promise * Intl ## Överlagringar Funktionsöverlagringar i TypeScript låter dig definiera flera funktionssignaturer för ett enda funktionsnamn, vilket gör det möjligt att definiera funktioner som kan anropas på flera sätt. Här är ett exempel: ```typescript // Overloads function sayHi(name: string): string; function sayHi(names: string[]): string[]; // Implementation function sayHi(name: unknown): unknown { if (typeof name === 'string') { return `Hi, ${name}!`; } else if (Array.isArray(name)) { return name.map(name => `Hi, ${name}!`); } throw new Error('Invalid value'); } sayHi('xx'); // Valid sayHi(['aa', 'bb']); // Valid ``` Här är ytterligare ett exempel på användning av funktionsöverlagringar inom en `class`: ```typescript class Greeter { message: string; constructor(message: string) { this.message = message; } // overload sayHi(name: string): string; sayHi(names: string[]): ReadonlyArray; // implementation sayHi(name: unknown): unknown { if (typeof name === 'string') { return `${this.message}, ${name}!`; } else if (Array.isArray(name)) { return name.map(name => `${this.message}, ${name}!`); } throw new Error('value is invalid'); } } console.log(new Greeter('Hello').sayHi('Simon')); ``` ## Sammanslagning och utökning Sammanslagning och utökning avser två olika koncept relaterade till att arbeta med typer och interface. Sammanslagning låter dig kombinera flera deklarationer med samma namn till en enda definition, till exempel när du definierar ett interface med samma namn flera gånger: ```typescript interface X { a: string; } interface X { b: number; } const person: X = { a: 'a', b: 7, }; ``` Utökning avser möjligheten att utöka eller ärva från befintliga typer eller interface för att skapa nya. Det är en mekanism för att lägga till ytterligare egenskaper eller metoder till en befintlig typ utan att ändra dess ursprungliga definition. Exempel: ```typescript interface Animal { name: string; eat(): void; } interface Bird extends Animal { sing(): void; } const dog: Bird = { name: 'Bird 1', eat() { console.log('Eating'); }, sing() { console.log('Singing'); }, }; ``` ## Skillnader mellan Type och Interface Deklarationssammanslagning (augmentering): Interface stöder deklarationssammanslagning, vilket innebär att du kan definiera flera interface med samma namn, och TypeScript kommer att slå samman dem till ett enda interface med de kombinerade egenskaperna och metoderna. Å andra sidan stöder typer inte deklarationssammanslagning. Detta kan vara användbart när du vill lägga till extra funktionalitet eller anpassa befintliga typer utan att ändra de ursprungliga definitionerna eller korrigera saknade eller felaktiga typer. ```typescript interface A { x: string; } interface A { y: string; } const j: A = { x: 'xx', y: 'yy', }; ``` Utökning av andra typer/interface: Både typer och interface kan utöka andra typer/interface, men syntaxen är annorlunda. Med interface använder du nyckelordet `extends` för att ärva egenskaper och metoder från andra interface. Ett interface kan dock inte utöka en komplex typ som en union-typ. ```typescript interface A { x: string; y: number; } interface B extends A { z: string; } const car: B = { x: 'x', y: 123, z: 'z', }; ``` För typer använder du operatorn & för att kombinera flera typer till en enda typ (intersection). ```typescript interface A { x: string; y: number; } type B = A & { j: string; }; const c: B = { x: 'x', y: 123, j: 'j', }; ``` Union- och Intersection-typer: Typer är mer flexibla när det gäller att definiera union- och intersection-typer. Med nyckelordet `type` kan du enkelt skapa union-typer med operatorn `|` och intersection-typer med operatorn `&`. Även om interface också kan representera union-typer indirekt, har de inget inbyggt stöd för intersection-typer. ```typescript type Department = 'dep-x' | 'dep-y'; // Union type Person = { name: string; age: number; }; type Employee = { id: number; department: Department; }; type EmployeeInfo = Person & Employee; // Intersection ``` Exempel med interface: ```typescript interface A { x: 'x'; } interface B { y: 'y'; } type C = A | B; // Union of interfaces ``` ## Klass ### Vanlig klasssyntax Nyckelordet `class` används i TypeScript för att definiera en klass. Nedan kan du se ett exempel: ```typescript class Person { private name: string; private age: number; constructor(name: string, age: number) { this.name = name; this.age = age; } public sayHi(): void { console.log( `Hello, my name is ${this.name} and I am ${this.age} years old.` ); } } ``` Nyckelordet `class` används för att definiera en klass som heter "Person". Klassen har två privata egenskaper: name av typen `string` och age av typen `number`. Konstruktorn definieras med nyckelordet `constructor`. Den tar name och age som parametrar och tilldelar dem till motsvarande egenskaper. Klassen har en `public` metod som heter sayHi som loggar ett hälsningsmeddelande. För att skapa en instans av en klass i TypeScript kan du använda nyckelordet `new` följt av klassnamnet, följt av parenteser `()`. Till exempel: ```typescript const myObject = new Person('John Doe', 25); myObject.sayHi(); // Output: Hello, my name is John Doe and I am 25 years old. ``` ### Konstruktor Konstruktorer är speciella metoder inom en klass som används för att initiera objektets egenskaper när en instans av klassen skapas. ```typescript class Person { public name: string; public age: number; constructor(name: string, age: number) { this.name = name; this.age = age; } sayHello() { console.log( `Hello, my name is ${this.name} and I'm ${this.age} years old.` ); } } const john = new Person('Simon', 17); john.sayHello(); ``` Det är möjligt att överlagra en konstruktor med följande syntax: ```typescript type Sex = 'm' | 'f'; class Person { name: string; age: number; sex: Sex; constructor(name: string, age: number, sex?: Sex); constructor(name: string, age: number, sex: Sex) { this.name = name; this.age = age; this.sex = sex ?? 'm'; } } const p1 = new Person('Simon', 17); const p2 = new Person('Alice', 22, 'f'); ``` I TypeScript är det möjligt att definiera flera konstruktoröverlagringar, men du kan bara ha en implementering som måste vara kompatibel med alla överlagringar. Detta kan uppnås genom att använda en valfri parameter. ```typescript class Person { name: string; age: number; constructor(); constructor(name: string); constructor(name: string, age: number); constructor(name?: string, age?: number) { this.name = name ?? 'Unknown'; this.age = age ?? 0; } displayInfo() { console.log(`Name: ${this.name}, Age: ${this.age}`); } } const person1 = new Person(); person1.displayInfo(); // Name: Unknown, Age: 0 const person2 = new Person('John'); person2.displayInfo(); // Name: John, Age: 0 const person3 = new Person('Jane', 25); person3.displayInfo(); // Name: Jane, Age: 25 ``` ### Privata och skyddade konstruktorer I TypeScript kan konstruktorer markeras som privata eller skyddade, vilket begränsar deras åtkomlighet och användning. Privata konstruktorer: Kan bara anropas inom själva klassen. Privata konstruktorer används ofta i scenarier där du vill upprätthålla ett singleton-mönster eller begränsa skapandet av instanser till en fabriksmetod inom klassen. Skyddade konstruktorer: Skyddade konstruktorer är användbara när du vill skapa en basklass som inte ska instansieras direkt men kan utökas av underklasser. ```typescript class BaseClass { protected constructor() {} } class DerivedClass extends BaseClass { private value: number; constructor(value: number) { super(); this.value = value; } } // Attempting to instantiate the base class directly will result in an error // const baseObj = new BaseClass(); // Error: Constructor of class 'BaseClass' is protected. // Create an instance of the derived class const derivedObj = new DerivedClass(10); ``` ### Åtkomstmodifierare Åtkomstmodifierarna `private`, `protected` och `public` används för att styra synligheten och åtkomsten till klassmedlemmar, såsom egenskaper och metoder, i TypeScript-klasser. Dessa modifierare är viktiga för att upprätthålla inkapsling och för att etablera gränser för åtkomst och modifiering av en klass interna tillstånd. Modifieraren `private` begränsar åtkomsten till klassmedlemmen enbart inom den innehållande klassen. Modifieraren `protected` tillåter åtkomst till klassmedlemmen inom den innehållande klassen och dess härledda klasser. Modifieraren `public` ger obegränsad åtkomst till klassmedlemmen och tillåter att den nås från var som helst. ### Get och Set Getters och setters är speciella metoder som låter dig definiera anpassat åtkomst- och ändringsbeteende för klassegenskaper. De gör det möjligt att kapsla in det interna tillståndet hos ett objekt och tillhandahålla ytterligare logik vid hämtning eller inställning av egenskapsvärden. I TypeScript definieras getters och setters med nyckelorden `get` respektive `set`. Här är ett exempel: ```typescript class MyClass { private _myProperty: string; constructor(value: string) { this._myProperty = value; } get myProperty(): string { return this._myProperty; } set myProperty(value: string) { this._myProperty = value; } } ``` ### Auto-accessorer i klasser TypeScript version 4.9 lägger till stöd för auto-accessorer, en kommande ECMAScript-funktion. De liknar klassegenskaper men deklareras med nyckelordet "accessor". ```typescript class Animal { accessor name: string; constructor(name: string) { this.name = name; } } ``` Auto-accessorer "avsockras" till privata `get`- och `set`-accessorer som opererar på en otillgänglig egenskap. ```typescript class Animal { #__name: string; get name() { return this.#__name; } set name(value: string) { this.#__name = name; } constructor(name: string) { this.name = name; } } ``` ### this I TypeScript refererar nyckelordet `this` till den aktuella instansen av en klass inom dess metoder eller konstruktorer. Det ger dig möjlighet att komma åt och modifiera klassens egenskaper och metoder inifrån dess eget scope. Det erbjuder ett sätt att komma åt och manipulera det interna tillståndet hos ett objekt inom dess egna metoder. ```typescript class Person { private name: string; constructor(name: string) { this.name = name; } public introduce(): void { console.log(`Hello, my name is ${this.name}.`); } } const person1 = new Person('Alice'); person1.introduce(); // Hello, my name is Alice. ``` ### Parameteregenskaper Parameteregenskaper gör det möjligt att deklarera och initiera klassegenskaper direkt i konstruktorns parametrar, vilket undviker överflödig kod. Exempel: ```typescript class Person { constructor( private name: string, public age: number ) { // The "private" and "public" keywords in the constructor // automatically declare and initialize the corresponding class properties. } public introduce(): void { console.log( `Hello, my name is ${this.name} and I am ${this.age} years old.` ); } } const person = new Person('Alice', 25); person.introduce(); ``` ### Abstrakta klasser Abstrakta klasser används i TypeScript främst för arv; de erbjuder ett sätt att definiera gemensamma egenskaper och metoder som kan ärvas av underklasser. Detta är användbart när du vill definiera gemensamt beteende och säkerställa att underklasser implementerar vissa metoder. De ger ett sätt att skapa en hierarki av klasser där den abstrakta basklassen tillhandahåller ett delat gränssnitt och gemensam funktionalitet för underklasserna. ```typescript abstract class Animal { protected name: string; constructor(name: string) { this.name = name; } abstract makeSound(): void; } class Cat extends Animal { makeSound(): void { console.log(`${this.name} meows.`); } } const cat = new Cat('Whiskers'); cat.makeSound(); // Output: Whiskers meows. ``` ### Med generics Klasser med generics gör det möjligt att definiera återanvändbara klasser som kan arbeta med olika typer. ```typescript class Container { private item: T; constructor(item: T) { this.item = item; } getItem(): T { return this.item; } setItem(item: T): void { this.item = item; } } const container1 = new Container(42); console.log(container1.getItem()); // 42 const container2 = new Container('Hello'); container2.setItem('World'); console.log(container2.getItem()); // World ``` ### Dekoratörer Dekoratörer tillhandahåller en mekanism för att lägga till metadata, ändra beteende, validera eller utöka funktionaliteten hos målelementet. De är funktioner som körs vid körning. Flera dekoratörer kan tillämpas på en deklaration. Dekoratörer är experimentella funktioner, och följande exempel är bara kompatibla med TypeScript version 5 eller senare med ES6. För TypeScript-versioner före 5 bör de aktiveras med egenskapen `experimentalDecorators` i din `tsconfig.json` eller genom att använda `--experimentalDecorators` på kommandoraden (men följande exempel kommer inte att fungera). Några vanliga användningsfall för dekoratörer inkluderar: * Övervakning av egenskapsändringar. * Övervakning av metodanrop. * Tillägg av extra egenskaper eller metoder. * Validering vid körning. * Automatisk serialisering och deserialisering. * Loggning. * Auktorisering och autentisering. * Felhantering. Observera: Dekoratörer för version 5 tillåter inte dekorering av parametrar. Typer av dekoratörer: #### Klassdekoratörer Klassdekoratörer är användbara för att utöka en befintlig klass, till exempel genom att lägga till egenskaper eller metoder, eller samla instanser av en klass. I följande exempel lägger vi till en `toString`-metod som konverterar klassen till en strängrepresentation. ```typescript type Constructor = new (...args: any[]) => T; function toString( Value: Class, context: ClassDecoratorContext ) { return class extends Value { constructor(...args: any[]) { super(...args); console.log(JSON.stringify(this)); console.log(JSON.stringify(context)); } }; } @toString class Person { name: string; constructor(name: string) { this.name = name; } greet() { return 'Hello, ' + this.name; } } const person = new Person('Simon'); /* Logs: {"name":"Simon"} {"kind":"class","name":"Person"} */ ``` #### Egenskapsdekoratör Egenskapsdekoratörer är användbara för att ändra beteendet hos en egenskap, till exempel genom att ändra initieringsvärden. I följande kod har vi ett skript som ställer in en egenskap till att alltid vara i versaler: ```typescript function upperCase( target: undefined, context: ClassFieldDecoratorContext ) { return function (this: T, value: string) { return value.toUpperCase(); }; } class MyClass { @upperCase prop1 = 'hello!'; } console.log(new MyClass().prop1); // Logs: HELLO! ``` #### Metoddekoratör Metoddekoratörer låter dig ändra eller förbättra beteendet hos metoder. Nedan följer ett exempel på en enkel logger: ```typescript function log( target: (this: This, ...args: Args) => Return, context: ClassMethodDecoratorContext< This, (this: This, ...args: Args) => Return > ) { const methodName = String(context.name); function replacementMethod(this: This, ...args: Args): Return { console.log(`LOG: Entering method '${methodName}'.`); const result = target.call(this, ...args); console.log(`LOG: Exiting method '${methodName}'.`); return result; } return replacementMethod; } class MyClass { @log sayHello() { console.log('Hello!'); } } new MyClass().sayHello(); ``` Det loggar: ```shell LOG: Entering method 'sayHello'. Hello! LOG: Exiting method 'sayHello'. ``` #### Getter- och setter-dekoratörer Getter- och setter-dekoratörer låter dig ändra eller förbättra beteendet hos klass-accessorer. De är användbara, till exempel, för validering av egenskapstilldelningar. Här är ett enkelt exempel på en getter-dekoratör: ```typescript function range(min: number, max: number) { return function ( target: (this: This) => Return, context: ClassGetterDecoratorContext ) { return function (this: This): Return { const value = target.call(this); if (value < min || value > max) { throw 'Invalid'; } Object.defineProperty(this, context.name, { value, enumerable: true, }); return value; }; }; } class MyClass { private _value = 0; constructor(value: number) { this._value = value; } @range(1, 100) get getValue(): number { return this._value; } } const obj = new MyClass(10); console.log(obj.getValue); // Valid: 10 const obj2 = new MyClass(999); console.log(obj2.getValue); // Throw: Invalid! ``` #### Dekoratörmetadata Dekoratörmetadata förenklar processen för dekoratörer att tillämpa och använda metadata i valfri klass. De kan komma åt en ny metadataegenskap på kontextobjektet, som kan fungera som en nyckel för både primitiva värden och objekt. Metadatainformation kan nås på klassen via `Symbol.metadata`. Metadata kan användas för olika ändamål, till exempel felsökning, serialisering eller beroendeinjektion med dekoratörer. ```typescript //@ts-ignore Symbol.metadata ??= Symbol('Symbol.metadata'); // Simple polify type Context = | ClassFieldDecoratorContext | ClassAccessorDecoratorContext | ClassMethodDecoratorContext; // Context contains property metadata: DecoratorMetadata function setMetadata(_target: any, context: Context) { // Set the metadata object with a primitive value context.metadata[context.name] = true; } class MyClass { @setMetadata a = 123; @setMetadata accessor b = 'b'; @setMetadata fn() {} } const metadata = MyClass[Symbol.metadata]; // Get metadata information console.log(JSON.stringify(metadata)); // {"bar":true,"baz":true,"foo":true} ``` ### Arv Arv avser mekanismen genom vilken en klass kan ärva egenskaper och metoder från en annan klass, känd som basklassen eller superklassen. Den härledda klassen, även kallad barnklassen eller underklassen, kan utöka och specialisera basklassens funktionalitet genom att lägga till nya egenskaper och metoder eller åsidosätta befintliga. ```typescript class Animal { name: string; constructor(name: string) { this.name = name; } speak(): void { console.log('The animal makes a sound'); } } class Dog extends Animal { breed: string; constructor(name: string, breed: string) { super(name); this.breed = breed; } speak(): void { console.log('Woof! Woof!'); } } // Create an instance of the base class const animal = new Animal('Generic Animal'); animal.speak(); // The animal makes a sound // Create an instance of the derived class const dog = new Dog('Max', 'Labrador'); dog.speak(); // Woof! Woof!" ``` TypeScript stöder inte multipelt arv i traditionell bemärkelse utan tillåter istället arv från en enda basklass. TypeScript stöder flera gränssnitt. Ett gränssnitt kan definiera ett kontrakt för strukturen hos ett objekt, och en klass kan implementera flera gränssnitt. Detta gör det möjligt för en klass att ärva beteende och struktur från flera källor. ```typescript interface Flyable { fly(): void; } interface Swimmable { swim(): void; } class FlyingFish implements Flyable, Swimmable { fly() { console.log('Flying...'); } swim() { console.log('Swimming...'); } } const flyingFish = new FlyingFish(); flyingFish.fly(); flyingFish.swim(); ``` Nyckelordet `class` i TypeScript, liknande JavaScript, kallas ofta för syntaktiskt socker. Det introducerades i ECMAScript 2015 (ES6) för att erbjuda en mer välbekant syntax för att skapa och arbeta med objekt på ett klassbaserat sätt. Det är dock viktigt att notera att TypeScript, som en utökning av JavaScript, slutligen kompileras ner till JavaScript, som i grunden förblir prototypbaserat. ### Statiska medlemmar TypeScript har statiska medlemmar. För att komma åt de statiska medlemmarna i en klass kan du använda klassnamnet följt av en punkt, utan att behöva skapa ett objekt. ```typescript class OfficeWorker { static memberCount: number = 0; constructor(private name: string) { OfficeWorker.memberCount++; } } const w1 = new OfficeWorker('James'); const w2 = new OfficeWorker('Simon'); const total = OfficeWorker.memberCount; console.log(total); // 2 ``` ### Egenskapsinitiering Det finns flera sätt att initiera egenskaper för en klass i TypeScript: Inline: I följande exempel kommer dessa initiala värden att användas när en instans av klassen skapas. ```typescript class MyClass { property1: string = 'default value'; property2: number = 42; } ``` I konstruktorn: ```typescript class MyClass { property1: string; property2: number; constructor() { this.property1 = 'default value'; this.property2 = 42; } } ``` Använda konstruktorparametrar: ```typescript class MyClass { constructor( private property1: string = 'default value', public property2: number = 42 ) { // There is no need to assign the values to the properties explicitly. } log() { console.log(this.property2); } } const x = new MyClass(); x.log(); ``` ### Metodöverlagring Metodöverlagring gör det möjligt för en klass att ha flera metoder med samma namn men olika parametertyper eller ett annat antal parametrar. Detta gör att vi kan anropa en metod på olika sätt baserat på de argument som skickas. ```typescript class MyClass { add(a: number, b: number): number; // Overload signature 1 add(a: string, b: string): string; // Overload signature 2 add(a: number | string, b: number | string): number | string { if (typeof a === 'number' && typeof b === 'number') { return a + b; } if (typeof a === 'string' && typeof b === 'string') { return a.concat(b); } throw new Error('Invalid arguments'); } } const r = new MyClass(); console.log(r.add(10, 5)); // Logs 15 ``` ## Generics Generics gör det möjligt att skapa återanvändbara komponenter och funktioner som kan arbeta med flera typer. Med generics kan du parameterisera typer, funktioner och gränssnitt, vilket gör att de kan arbeta med olika typer utan att explicit specificera dem i förväg. Generics gör det möjligt att göra koden mer flexibel och återanvändbar. ### Generisk typ För att definiera en generisk typ använder du vinkelparenteser (`<>`) för att specificera typparametrarna, till exempel: ```typescript function identity(arg: T): T { return arg; } const a = identity('x'); const b = identity(123); const getLen = (data: ReadonlyArray) => data.length; const len = getLen([1, 2, 3]); ``` ### Generiska klasser Generics kan även tillämpas på klasser, på så sätt kan de arbeta med flera typer genom att använda typparametrar. Detta är användbart för att skapa återanvändbara klassdefinitioner som kan arbeta med olika datatyper samtidigt som typsäkerheten bibehålls. ```typescript class Container { private item: T; constructor(item: T) { this.item = item; } getItem(): T { return this.item; } } const numberContainer = new Container(123); console.log(numberContainer.getItem()); // 123 const stringContainer = new Container('hello'); console.log(stringContainer.getItem()); // hello ``` ### Generiska begränsningar Generiska parametrar kan begränsas med nyckelordet `extends` följt av en typ eller ett gränssnitt som typparametern måste uppfylla. I följande exempel måste T innehålla en egenskap `length` för att vara giltig: ```typescript const printLen = (value: T): void => { console.log(value.length); }; printLen('Hello'); // 5 printLen([1, 2, 3]); // 3 printLen({ length: 10 }); // 10 printLen(123); // Invalid ``` En intressant funktion hos generics som introducerades i version 3.4 RC är typinferens för högre ordningens funktioner, vilket introducerade propagerade generiska typargument: ```typescript declare function pipe( ab: (...args: A) => B, bc: (b: B) => C ): (...args: A) => C; declare function list(a: T): T[]; declare function box(x: V): { value: V }; const listBox = pipe(list, box); // (a: T) => { value: T[] } const boxList = pipe(box, list); // (x: V) => { value: V }[] ``` Denna funktionalitet möjliggör enklare typsäker punktfri programmering (pointfree style) vilket är vanligt inom funktionell programmering. ### Generisk kontextuell avsmalning Kontextuell avsmalning för generics är mekanismen i TypeScript som gör det möjligt för kompilatorn att smalna av typen för en generisk parameter baserat på det sammanhang där den används. Det är användbart vid arbete med generiska typer i villkorssatser: ```typescript function process(value: T): void { if (typeof value === 'string') { // Value is narrowed down to type 'string' console.log(value.length); } else if (typeof value === 'number') { // Value is narrowed down to type 'number' console.log(value.toFixed(2)); } } process('hello'); // 5 process(3.14159); // 3.14 ``` ## Raderade strukturella typer I TypeScript behöver objekt inte matcha en specifik, exakt typ. Till exempel, om vi skapar ett objekt som uppfyller ett gränssnitts krav, kan vi använda det objektet på platser där det gränssnittet krävs, även om det inte finns någon explicit koppling mellan dem. Exempel: ```typescript type NameProp1 = { prop1: string; }; function log(x: NameProp1) { console.log(x.prop1); } const obj = { prop2: 123, prop1: 'Origin', }; log(obj); // Valid ``` ## Namnrymder I TypeScript används namnrymder (namespaces) för att organisera kod i logiska behållare, förhindra namnkollisioner och ge ett sätt att gruppera relaterad kod tillsammans. Användningen av nyckelordet `export` tillåter åtkomst till namnrymden i "utomstående" moduler. ```typescript export namespace MyNamespace { export interface MyInterface1 { prop1: boolean; } export interface MyInterface2 { prop2: string; } } const a: MyNamespace.MyInterface1 = { prop1: true, }; ``` ## Symboler Symboler är en primitiv datatyp som representerar ett oföränderligt värde som garanterat är globalt unikt under hela programmets livstid. Symboler kan användas som nycklar för objektegenskaper och ger ett sätt att skapa icke-uppräkningsbara egenskaper. ```typescript const key1: symbol = Symbol('key1'); const key2: symbol = Symbol('key2'); const obj = { [key1]: 'value 1', [key2]: 'value 2', }; console.log(obj[key1]); // value 1 console.log(obj[key2]); // value 2 ``` I WeakMaps och WeakSets är symboler nu tillåtna som nycklar. ## Trippelsnedstreck-direktiv Trippelsnedstreck-direktiv är speciella kommentarer som ger instruktioner till kompilatorn om hur en fil ska bearbetas. Dessa direktiv börjar med tre på varandra följande snedstreck (`///`) och placeras vanligtvis överst i en TypeScript-fil och har ingen effekt på körbeteendet. Trippelsnedstreck-direktiv används för att referera till externa beroenden, specificera modulinläsningsbeteende, aktivera/inaktivera vissa kompilatorfunktioner, med mera. Några exempel: Referera till en deklarationsfil: ```typescript /// ``` Ange modulformat: ```typescript /// ``` Aktivera kompilatoralternativ, i följande exempel strikt läge: ```typescript /// ``` ## Typmanipulation ### Skapa typer från typer Det är möjligt att skapa nya typer genom att komponera, manipulera eller transformera befintliga typer. Intersektionstyper (`&`): Gör det möjligt att kombinera flera typer till en enda typ: ```typescript type A = { foo: number }; type B = { bar: string }; type C = A & B; // Intersection of A and B const obj: C = { foo: 42, bar: 'hello' }; ``` Unionstyper (`|`): Gör det möjligt att definiera en typ som kan vara en av flera typer: ```typescript type Result = string | number; const value1: Result = 'hello'; const value2: Result = 42; ``` Mappade typer: Gör det möjligt att transformera egenskaperna hos en befintlig typ för att skapa en ny typ: ```typescript type Mutable = { readonly [P in keyof T]: T[P]; }; type Person = { name: string; age: number; }; type ImmutablePerson = Mutable; // Properties become read-only ``` Villkorliga typer: Gör det möjligt att skapa typer baserat på vissa villkor: ```typescript type ExtractParam = T extends (param: infer P) => any ? P : never; type MyFunction = (name: string) => number; type ParamType = ExtractParam; // string ``` ### Indexerade åtkomsttyper I TypeScript är det möjligt att komma åt och manipulera typerna av egenskaper inom en annan typ med hjälp av ett index, `Type[Key]`. ```typescript type Person = { name: string; age: number; }; type AgeType = Person['age']; // number ``` ```typescript type MyTuple = [string, number, boolean]; type MyType = MyTuple[2]; // boolean ``` ### Verktygstyper Flera inbyggda verktygstyper kan användas för att manipulera typer. Nedan följer en lista över de mest använda: #### Awaited\ Konstruerar en typ som rekursivt packar upp Promise-typer. ```typescript type A = Awaited>; // string ``` #### Partial\ Konstruerar en typ där alla egenskaper i T är satta som valfria. ```typescript type Person = { name: string; age: number; }; type A = Partial; // { name?: string | undefined; age?: number | undefined; } ``` #### Required\ Konstruerar en typ där alla egenskaper i T är satta som obligatoriska. ```typescript type Person = { name?: string; age?: number; }; type A = Required; // { name: string; age: number; } ``` #### Readonly\ Konstruerar en typ där alla egenskaper i T är satta som skrivskyddade. ```typescript type Person = { name: string; age: number; }; type A = Readonly; const a: A = { name: 'Simon', age: 17 }; a.name = 'John'; // Invalid ``` #### Record\ Konstruerar en typ med en uppsättning egenskaper K av typen T. ```typescript type Product = { name: string; price: number; }; const products: Record = { apple: { name: 'Apple', price: 0.5 }, banana: { name: 'Banana', price: 0.25 }, }; console.log(products.apple); // { name: 'Apple', price: 0.5 } ``` #### Pick\ Konstruerar en typ genom att välja ut de angivna egenskaperna K från T. ```typescript type Product = { name: string; price: number; }; type Price = Pick; // { price: number; } ``` #### Omit\ Konstruerar en typ genom att utelämna de angivna egenskaperna K från T. ```typescript type Product = { name: string; price: number; }; type Name = Omit; // { name: string; } ``` #### Exclude\ Konstruerar en typ genom att exkludera alla värden av typen U från T. ```typescript type Union = 'a' | 'b' | 'c'; type MyType = Exclude; // b ``` #### Extract\ Konstruerar en typ genom att extrahera alla värden av typen U från T. ```typescript type Union = 'a' | 'b' | 'c'; type MyType = Extract; // a | c ``` #### NonNullable\ Konstruerar en typ genom att exkludera null och undefined från T. ```typescript type Union = 'a' | null | undefined | 'b'; type MyType = NonNullable; // 'a' | 'b' ``` #### Parameters\ Extraherar parametertyperna för en funktionstyp T. ```typescript type Func = (a: string, b: number) => void; type MyType = Parameters; // [a: string, b: number] ``` #### ConstructorParameters\ Extraherar parametertyperna för en konstruktorfunktionstyp T. ```typescript class Person { constructor( public name: string, public age: number ) {} } type PersonConstructorParams = ConstructorParameters; // [name: string, age: number] const params: PersonConstructorParams = ['John', 30]; const person = new Person(...params); console.log(person); // Person { name: 'John', age: 30 } ``` #### ReturnType\ Extraherar returtypen för en funktionstyp T. ```typescript type Func = (name: string) => number; type MyType = ReturnType; // number ``` #### InstanceType\ Extraherar instanstypen för en klasstyp T. ```typescript class Person { name: string; constructor(name: string) { this.name = name; } sayHello() { console.log(`Hello, my name is ${this.name}!`); } } type PersonInstance = InstanceType; const person: PersonInstance = new Person('John'); person.sayHello(); // Hello, my name is John! ``` #### ThisParameterType\ Extraherar typen av 'this'-parametern från en funktionstyp T. ```typescript interface Person { name: string; greet(this: Person): void; } type PersonThisType = ThisParameterType; // Person ``` #### OmitThisParameter\ Tar bort 'this'-parametern från en funktionstyp T. ```typescript function capitalize(this: String) { return this[0].toUpperCase + this.substring(1).toLowerCase(); } type CapitalizeType = OmitThisParameter; // () => string ``` #### ThisType\ Fungerar som en markör för en kontextuell `this`-typ. ```typescript type Logger = { log: (error: string) => void; }; let helperFunctions: { [name: string]: Function } & ThisType = { hello: function () { this.log('some error'); // Valid as "log" is a part of "this". this.update(); // Invalid }, }; ``` #### Uppercase\ Gör namnet på indatatypen T till versaler. ```typescript type MyType = Uppercase<'abc'>; // "ABC" ``` #### Lowercase\ Gör namnet på indatatypen T till gemener. ```typescript type MyType = Lowercase<'ABC'>; // "abc" ``` #### Capitalize\ Gör första bokstaven i namnet på indatatypen T till versal. ```typescript type MyType = Capitalize<'abc'>; // "Abc" ``` #### Uncapitalize\ Gör första bokstaven i namnet på indatatypen T till gemen. ```typescript type MyType = Uncapitalize<'Abc'>; // "abc" ``` #### NoInfer\ NoInfer är en verktygstyp utformad för att blockera automatisk typinferens inom ramen för en generisk funktion. Exempel: ```typescript // Automatic inference of types within the scope of a generic function. function fn(x: T[], y: T) { return x.concat(y); } const r = fn(['a', 'b'], 'c'); // Type here is ("a" | "b" | "c")[] ``` Med NoInfer: ```typescript // Example function that uses NoInfer to prevent type inference function fn2(x: T[], y: NoInfer) { return x.concat(y); } const r2 = fn2(['a', 'b'], 'c'); // Error: Type Argument of type '"c"' is not assignable to parameter of type '"a" | "b"'. ``` ## Övrigt ### Fel och undantagshantering TypeScript låter dig fånga och hantera fel med hjälp av JavaScripts standardmekanismer för felhantering: Try-Catch-Finally-block: ```typescript try { // Code that might throw an error } catch (error) { // Handle the error } finally { // Code that always executes, finally is optional } ``` Du kan också hantera olika typer av fel: ```typescript try { // Code that might throw different types of errors } catch (error) { if (error instanceof TypeError) { // Handle TypeError } else if (error instanceof RangeError) { // Handle RangeError } else { // Handle other errors } } ``` Anpassade feltyper: Det är möjligt att specificera mer specifika fel genom att utöka `Error`-klassen: ```typescript class CustomError extends Error { constructor(message: string) { super(message); this.name = 'CustomError'; } } throw new CustomError('This is a custom error.'); ``` ### Mixin-klasser Mixin-klasser låter dig kombinera och komponera beteende från flera klasser till en enda klass. De erbjuder ett sätt att återanvända och utöka funktionalitet utan behov av djupa arvskedjor. ```typescript abstract class Identifiable { name: string = ''; logId() { console.log('id:', this.name); } } abstract class Selectable { selected: boolean = false; select() { this.selected = true; console.log('Select'); } deselect() { this.selected = false; console.log('Deselect'); } } class MyClass { constructor() {} } // Extend MyClass to include the behavior of Identifiable and Selectable interface MyClass extends Identifiable, Selectable {} // Function to apply mixins to a class function applyMixins(source: any, baseCtors: any[]) { baseCtors.forEach(baseCtor => { Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => { let descriptor = Object.getOwnPropertyDescriptor( baseCtor.prototype, name ); if (descriptor) { Object.defineProperty(source.prototype, name, descriptor); } }); }); } // Apply the mixins to MyClass applyMixins(MyClass, [Identifiable, Selectable]); let o = new MyClass(); o.name = 'abc'; o.logId(); o.select(); ``` ### Asynkrona språkfunktioner Eftersom TypeScript är en superset av JavaScript har det inbyggda asynkrona språkfunktioner från JavaScript som: Promises: Promises är ett sätt att hantera asynkrona operationer och deras resultat med hjälp av metoder som `.then()` och `.catch()` för att hantera framgångs- och feltillstånd. Läs mer: [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) Async/await: Nyckelorden async/await erbjuder en mer synkront utseende syntax för att arbeta med Promises. Nyckelordet `async` används för att definiera en asynkron funktion, och nyckelordet `await` används inom en async-funktion för att pausa körningen tills ett Promise har lösts eller avvisats. Läs mer: [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function) [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await) Följande API:er stöds väl i TypeScript: Fetch API: [https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) Web Workers: [https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API) Shared Workers: [https://developer.mozilla.org/en-US/docs/Web/API/SharedWorker](https://developer.mozilla.org/en-US/docs/Web/API/SharedWorker) WebSocket: [https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API) ### Iteratorer och generatorer Både iteratorer och generatorer stöds väl i TypeScript. Iteratorer är objekt som implementerar iteratorprotokollet och erbjuder ett sätt att komma åt element i en samling eller sekvens ett i taget. Det är en struktur som innehåller en pekare till nästa element i iterationen. De har en `next()`-metod som returnerar nästa värde i sekvensen tillsammans med en boolean som anger om sekvensen är `done` (klar). ```typescript class NumberIterator implements Iterable { private current: number; constructor( private start: number, private end: number ) { this.current = start; } public next(): IteratorResult { if (this.current <= this.end) { const value = this.current; this.current++; return { value, done: false }; } else { return { value: undefined, done: true }; } } [Symbol.iterator](): Iterator { return this; } } const iterator = new NumberIterator(1, 3); for (const num of iterator) { console.log(num); } ``` Generatorer är speciella funktioner definierade med syntaxen `function*` som förenklar skapandet av iteratorer. De använder nyckelordet `yield` för att definiera sekvensen av värden och pausar och återupptar automatiskt körningen när värden efterfrågas. Generatorer gör det enklare att skapa iteratorer och är särskilt användbara för att arbeta med stora eller oändliga sekvenser. Exempel: ```typescript function* numberGenerator(start: number, end: number): Generator { for (let i = start; i <= end; i++) { yield i; } } const generator = numberGenerator(1, 5); for (const num of generator) { console.log(num); } ``` TypeScript stöder också asynkrona iteratorer och asynkrona generatorer. Läs mer: [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Generator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Generator) [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Iterator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Iterator) ### TsDocs JSDoc-referens När man arbetar med en JavaScript-kodbas är det möjligt att hjälpa TypeScript att härleda rätt typ genom att använda JSDoc-kommentarer med ytterligare annotationer för att ge typinformation. Exempel: ```typescript /** * Computes the power of a given number * @constructor * @param {number} base – The base value of the expression * @param {number} exponent – The exponent value of the expression */ function power(base: number, exponent: number) { return Math.pow(base, exponent); } power(10, 2); // function power(base: number, exponent: number): number ``` Fullständig dokumentation finns på denna länk: [https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html](https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html) Från version 3.7 är det möjligt att generera .d.ts-typdefinitioner från JavaScript JSDoc-syntax. Mer information finns här: [https://www.typescriptlang.org/docs/handbook/declaration-files/dts-from-js.html](https://www.typescriptlang.org/docs/handbook/declaration-files/dts-from-js.html) ### @types Paket under @types-organisationen är speciella paketnamnkonventioner som används för att tillhandahålla typdefinitioner för befintliga JavaScript-bibliotek eller moduler. Till exempel genom att använda: ```shell npm install --save-dev @types/lodash ``` Installeras typdefinitionerna för `lodash` i ditt nuvarande projekt. För att bidra till typdefinitionerna för @types-paket, skicka in en pull request till [https://github.com/DefinitelyTyped/DefinitelyTyped](https://github.com/DefinitelyTyped/DefinitelyTyped). ### JSX JSX (JavaScript XML) är en utökning av JavaScript-språksyntaxen som låter dig skriva HTML-liknande kod i dina JavaScript- eller TypeScript-filer. Det används vanligtvis i React för att definiera HTML-strukturen. TypeScript utökar JSX:s kapacitet genom att tillhandahålla typkontroll och statisk analys. För att använda JSX behöver du ställa in kompileringsalternativet `jsx` i din `tsconfig.json`-fil. Två vanliga konfigurationsalternativ: * "preserve": genererar .jsx-filer med JSX oförändrad. Detta alternativ talar om för TypeScript att behålla JSX-syntaxen som den är och inte transformera den under kompileringsprocessen. Du kan använda detta alternativ om du har ett separat verktyg, som Babel, som hanterar transformationen. * "react": aktiverar TypeScripts inbyggda JSX-transformation. React.createElement kommer att användas. Alla alternativ finns tillgängliga här: [https://www.typescriptlang.org/tsconfig#jsx](https://www.typescriptlang.org/tsconfig#jsx) ### ES6-moduler TypeScript stöder ES6 (ECMAScript 2015) och många efterföljande versioner. Det innebär att du kan använda ES6-syntax, såsom pilfunktioner, mallsträngar, klasser, moduler, destrukturering och mer. För att aktivera ES6-funktioner i ditt projekt kan du ange egenskapen `target` i tsconfig.json. Ett konfigurationsexempel: ```json { "compilerOptions": { "target": "es6", "module": "es6", "moduleResolution": "node", "sourceMap": true, "outDir": "dist" }, "include": ["src"] } ``` ### ES7 exponentiationsoperator Exponentiationsoperatorn (`**`) beräknar värdet som erhålls genom att upphöja den första operanden till den andra operandens potens. Den fungerar på liknande sätt som `Math.pow()`, men med den ytterligare förmågan att acceptera BigInts som operander. TypeScript stöder denna operator fullt ut genom att använda `es2016` eller en senare version som `target` i din tsconfig.json-fil. ```typescript console.log(2 ** (2 ** 2)); // 16 ``` ### for-await-of-satsen Detta är en JavaScript-funktion som stöds fullt ut i TypeScript och som låter dig iterera över asynkrona iterbara objekt från målversion es2018. ```typescript async function* asyncNumbers(): AsyncIterableIterator { yield Promise.resolve(1); yield Promise.resolve(2); yield Promise.resolve(3); } (async () => { for await (const num of asyncNumbers()) { console.log(num); } })(); ``` ### Metaegenskapen new.target Du kan i TypeScript använda metaegenskapen `new.target` som gör det möjligt att avgöra om en funktion eller konstruktor anropades med new-operatorn. Det låter dig upptäcka om ett objekt skapades som ett resultat av ett konstruktoranrop. ```typescript class Parent { constructor() { console.log(new.target); // Logs the constructor function used to create an instance } } class Child extends Parent { constructor() { super(); } } const parentX = new Parent(); // [Function: Parent] const child = new Child(); // [Function: Child] ``` ### Dynamiska importuttryck Det är möjligt att villkorligt ladda moduler eller lat-ladda dem vid behov med hjälp av ECMAScript-förslaget för dynamisk import som stöds i TypeScript. Syntaxen för dynamiska importuttryck i TypeScript är följande: ```typescript async function renderWidget() { const container = document.getElementById('widget'); if (container !== null) { const widget = await import('./widget'); // Dynamic import widget.render(container); } } renderWidget(); ``` ### "tsc –watch" Detta kommando startar TypeScript-kompilatorn med parametern `--watch`, med möjligheten att automatiskt kompilera om TypeScript-filer när de ändras. ```shell tsc --watch ``` Från och med TypeScript version 4.9 förlitar sig filövervakning främst på filsystemhändelser och faller automatiskt tillbaka till polling om en händelsebaserad övervakare inte kan upprättas. ### Non-null Assertion Operator Non-null Assertion Operator (Postfix !) även kallad Definite Assignment Assertions är en TypeScript-funktion som låter dig försäkra att en variabel eller egenskap inte är null eller undefined, även om TypeScripts statiska typanalys antyder att den kan vara det. Med denna funktion är det möjligt att ta bort all explicit kontroll. ```typescript type Person = { name: string; }; const printName = (person?: Person) => { console.log(`Name is ${person!.name}`); }; ``` ### Standarddeklarationer Standarddeklarationer används när en variabel eller parameter tilldelas ett standardvärde. Det innebär att om inget värde anges för den variabeln eller parametern, kommer standardvärdet att användas istället. ```typescript function greet(name: string = 'Anonymous'): void { console.log(`Hello, ${name}!`); } greet(); // Hello, Anonymous! greet('John'); // Hello, John! ``` ### Valfri kedjning (Optional Chaining) Den valfria kedjningsoperatorn `?.` fungerar som den vanliga punktoperatorn (`.`) för att komma åt egenskaper eller metoder. Den hanterar dock null- eller undefined-värden smidigt genom att avsluta uttrycket och returnera `undefined`, istället för att kasta ett fel. ```typescript type Person = { name: string; age?: number; address?: { street?: string; city?: string; }; }; const person: Person = { name: 'John', }; console.log(person.address?.city); // undefined ``` ### Nullish coalescing-operatorn Nullish coalescing-operatorn `??` returnerar högerledet om vänsterledet är `null` eller `undefined`; annars returnerar den vänsterledets värde. ```typescript const foo = null ?? 'foo'; console.log(foo); // foo const baz = 1 ?? 'baz'; const baz2 = 0 ?? 'baz'; console.log(baz); // 1 console.log(baz2); // 0 ``` ### Mallsträngslitteraltyper (Template Literal Types) Mallsträngslitteraltyper gör det möjligt att manipulera strängvärden på typnivå och generera nya strängtyper baserade på befintliga. De är användbara för att skapa mer uttrycksfulla och precisa typer från strängbaserade operationer. ```typescript type Department = 'engineering' | 'hr'; type Language = 'english' | 'spanish'; type Id = `${Department}-${Language}-id`; // "engineering-english-id" | "engineering-spanish-id" | "hr-english-id" | "hr-spanish-id" ``` ### Funktionsöverlagring Funktionsöverlagring låter dig definiera flera funktionssignaturer för samma funktionsnamn, var och en med olika parametertyper och returtyp. När du anropar en överlagrad funktion använder TypeScript de angivna argumenten för att avgöra rätt funktionssignatur: ```typescript function makeGreeting(name: string): string; function makeGreeting(names: string[]): string[]; function makeGreeting(person: unknown): unknown { if (typeof person === 'string') { return `Hi ${person}!`; } else if (Array.isArray(person)) { return person.map(name => `Hi, ${name}!`); } throw new Error('Unable to greet'); } makeGreeting('Simon'); makeGreeting(['Simone', 'John']); ``` ### Rekursiva typer En rekursiv typ är en typ som kan referera till sig själv. Detta är användbart för att definiera datastrukturer som har en hierarkisk eller rekursiv struktur (potentiellt oändlig nästning), såsom länkade listor, träd och grafer. ```typescript type ListNode = { data: T; next: ListNode | undefined; }; ``` ### Rekursiva villkorstyper Det är möjligt att definiera komplexa typrelationer med hjälp av logik och rekursion i TypeScript. Låt oss bryta ner det i enkla termer: Villkorstyper: låter dig definiera typer baserade på booleska villkor: ```typescript type CheckNumber = T extends number ? 'Number' : 'Not a number'; type A = CheckNumber<123>; // 'Number' type B = CheckNumber<'abc'>; // 'Not a number' ``` Rekursion: innebär en typdefinition som refererar till sig själv inom sin egen definition: ```typescript type Json = string | number | boolean | null | Json[] | { [key: string]: Json }; const data: Json = { prop1: true, prop2: 'prop2', prop3: { prop4: [], }, }; ``` Rekursiva villkorstyper kombinerar både villkorslogik och rekursion. Det innebär att en typdefinition kan bero på sig själv genom villkorslogik, vilket skapar komplexa och flexibla typrelationer. ```typescript type Flatten = T extends Array ? Flatten : T; type NestedArray = [1, [2, [3, 4], 5], 6]; type FlattenedArray = Flatten; // 2 | 3 | 4 | 5 | 1 | 6 ``` ### Stöd för ECMAScript-moduler i Node Node.js lade till stöd för ECMAScript-moduler från och med version 15.3.0, och TypeScript har haft stöd för ECMAScript-moduler i Node.js sedan version 4.7. Detta stöd kan aktiveras genom att använda egenskapen `module` med värdet `nodenext` i tsconfig.json-filen. Här är ett exempel: ```json { "compilerOptions": { "module": "nodenext", "outDir": "./lib", "declaration": true } } ``` Node.js stöder två filändelser för moduler: `.mjs` för ES-moduler och `.cjs` för CommonJS-moduler. Motsvarande filändelser i TypeScript är `.mts` för ES-moduler och `.cts` för CommonJS-moduler. När TypeScript-kompilatorn transpilerar dessa filer till JavaScript skapar den `.mjs`- och `.cjs`-filer. Om du vill använda ES-moduler i ditt projekt kan du ställa in egenskapen `type` till "module" i din package.json-fil. Detta instruerar Node.js att behandla projektet som ett ES-modulprojekt. Dessutom stöder TypeScript även typdeklarationer i .d.ts-filer. Dessa deklarationsfiler tillhandahåller typinformation för bibliotek eller moduler skrivna i TypeScript, vilket låter andra utvecklare använda dem med TypeScripts typkontroll och autokompletteringsfunktioner. ### Assertionsfunktioner I TypeScript är assertionsfunktioner funktioner som indikerar verifieringen av ett specifikt villkor baserat på sitt returvärde. I sin enklaste form undersöker en assert-funktion ett givet predikat och kastar ett fel när predikatet utvärderas till false. ```typescript function isNumber(value: unknown): asserts value is number { if (typeof value !== 'number') { throw new Error('Not a number'); } } ``` De kan också deklareras som funktionsuttryck: ```typescript type AssertIsNumber = (value: unknown) => asserts value is number; const isNumber: AssertIsNumber = value => { if (typeof value !== 'number') { throw new Error('Not a number'); } }; ``` Assertionsfunktioner delar likheter med typskydd (type guards). Typskydd introducerades ursprungligen för att utföra körtidskontroller och säkerställa typen av ett värde inom ett specifikt scope. Specifikt är ett typskydd en funktion som utvärderar ett typpredikat och returnerar ett booleskt värde som anger om predikatet är sant eller falskt. Detta skiljer sig något från assertionsfunktioner, där avsikten är att kasta ett fel snarare än att returnera false när predikatet inte uppfylls. Exempel på typskydd: ```typescript const isNumber = (value: unknown): value is number => typeof value === 'number'; ``` ### Variadiska tuppeltyper Variadiska tuppeltyper är en funktion som introducerades i TypeScript version 4.0. Låt oss börja med att repetera vad en tuppel är: En tuppeltyp är en array som har en definierad längd, och där typen av varje element är känd: ```typescript type Student = [string, number]; const [name, age]: Student = ['Simone', 20]; ``` Termen "variadisk" betyder obestämd aritet (accepterar ett variabelt antal argument). En variadisk tuppel är en tuppeltyp som har alla egenskaper som ovan men vars exakta form ännu inte är definierad: ```typescript type Bar = [boolean, ...T, number]; type A = Bar<[boolean]>; // [boolean, boolean, number] type B = Bar<['a', 'b']>; // [boolean, 'a', 'b', number] type C = Bar<[]>; // [boolean, number] ``` I den föregående koden kan vi se att tuppelns form definieras av den generiska typen `T` som skickas in. Variadiska tuppler kan acceptera flera generiska typer vilket gör dem mycket flexibla: ```typescript type Bar = [...T, boolean, ...G]; type A = Bar<[number], [string]>; // [number, boolean, string] type B = Bar<['a', 'b'], [boolean]>; // ["a", "b", boolean, boolean] ``` Med de nya variadiska tupplerna kan vi använda: * Spridningar i tuppeltypssyntax kan nu vara generiska, så vi kan representera högre ordningens operationer på tuppler och arrayer även när vi inte känner till de faktiska typerna vi opererar på. * Restelement kan förekomma var som helst i en tuppel. Exempel: ```typescript type Items = readonly unknown[]; function concat( arr1: T, arr2: U ): [...T, ...U] { return [...arr1, ...arr2]; } concat([1, 2, 3], ['4', '5', '6']); // [1, 2, 3, "4", "5", "6"] ``` ### Inkapslingstyper (Boxed types) Inkapslingstyper refererar till omslagsobjekten som används för att representera primitiva typer som objekt. Dessa omslagsobjekt tillhandahåller ytterligare funktionalitet och metoder som inte är direkt tillgängliga på de primitiva värdena. När du anropar en metod som `charAt` eller `normalize` på en primitiv `string`, omsluter JavaScript den i ett `String`-objekt, anropar metoden och kastar sedan bort objektet. Demonstration: ```typescript const originalNormalize = String.prototype.normalize; String.prototype.normalize = function () { console.log(this, typeof this); return originalNormalize.call(this); }; console.log('\u0041'.normalize()); ``` TypeScript representerar denna skillnad genom att tillhandahålla separata typer för primitiverna och deras motsvarande objektomslag: * string => String * number => Number * boolean => Boolean * symbol => Symbol * bigint => BigInt Inkapslingstyperna behövs vanligtvis inte. Undvik att använda inkapslingstyper och använd istället typer för primitiverna, till exempel `string` istället för `String`. ### Kovarians och kontravarians i TypeScript Kovarians och kontravarians används för att beskriva hur relationer fungerar vid hantering av arv eller tilldelning av typer. Kovarians innebär att en typrelation bevarar riktningen för arv eller tilldelning, så om en typ A är en subtyp av typ B, anses också en array av typ A vara en subtyp av en array av typ B. Det viktiga att notera här är att subtyprelationen bibehålls, vilket innebär att kovarians accepterar subtyper men inte accepterar supertyper. Kontravarians innebär att en typrelation vänder riktningen för arv eller tilldelning, så om en typ A är en subtyp av typ B, anses en array av typ B vara en subtyp av en array av typ A. Subtyprelationen är omvänd, vilket innebär att kontravarians accepterar supertyper men inte accepterar subtyper. Notera: Bivarians innebär att både supertyper och subtyper accepteras. Exempel: Låt oss säga att vi har ett utrymme för alla djur och ett separat utrymme bara för hundar. Vid kovarians kan du placera alla hundar i djurutrymmet eftersom hundar är en typ av djur. Men du kan inte placera alla djur i hundutrymmet eftersom det kan finnas andra djur inblandade. Vid kontravarians kan du inte placera alla djur i hundutrymmet eftersom djurutrymmet kan innehålla andra djur också. Däremot kan du placera alla hundar i djurutrymmet eftersom alla hundar också är djur. ```typescript // Covariance example class Animal { name: string; constructor(name: string) { this.name = name; } } class Dog extends Animal { breed: string; constructor(name: string, breed: string) { super(name); this.breed = breed; } } let animals: Animal[] = []; let dogs: Dog[] = []; // Covariance allows assigning subtype (Dog) array to supertype (Animal) array animals = dogs; dogs = animals; // Invalid: Type 'Animal[]' is not assignable to type 'Dog[]' // Contravariance example type Feed = (animal: T) => void; let feedAnimal: Feed = (animal: Animal) => { console.log(`Animal name: ${animal.name}`); }; let feedDog: Feed = (dog: Dog) => { console.log(`Dog name: ${dog.name}, Breed: ${dog.breed}`); }; // Contravariance allows assigning supertype (Animal) callback to subtype (Dog) callback feedDog = feedAnimal; feedAnimal = feedDog; // Invalid: Type 'Feed' is not assignable to type 'Feed'. ``` I TypeScript är typrelationer för arrayer kovarianta, medan typrelationer för funktionsparametrar är kontravarianta. Det innebär att TypeScript uppvisar både kovarians och kontravarians beroende på sammanhanget. #### Valfria variansannotationer för typparametrar Från och med TypeScript 4.7.0 kan vi använda nyckelorden `out` och `in` för att vara specifika med variansannotationer. För kovarians, använd nyckelordet `out`: ```typescript type AnimalCallback = () => T; // T is Covariant here ``` Och för kontravarians, använd nyckelordet `in`: ```typescript type AnimalCallback = (value: T) => void; // T is Contravariance here ``` ### Mallsträngsmönsterindexsignaturer Mallsträngsmönsterindexsignaturer gör det möjligt att definiera flexibla indexsignaturer med hjälp av mallsträngsmönster. Denna funktion gör det möjligt att skapa objekt som kan indexeras med specifika mönster av strängnycklar, vilket ger mer kontroll och specificitet vid åtkomst och manipulering av egenskaper. TypeScript tillåter från version 4.4 indexsignaturer för symboler och mallsträngsmönster. ```typescript const uniqueSymbol = Symbol('description'); type MyKeys = `key-${string}`; type MyObject = { [uniqueSymbol]: string; [key: MyKeys]: number; }; const obj: MyObject = { [uniqueSymbol]: 'Unique symbol key', 'key-a': 123, 'key-b': 456, }; console.log(obj[uniqueSymbol]); // Unique symbol key console.log(obj['key-a']); // 123 console.log(obj['key-b']); // 456 ``` ### satisfies-operatorn Operatorn `satisfies` låter dig kontrollera om en given typ uppfyller ett specifikt gränssnitt eller villkor. Med andra ord säkerställer den att en typ har alla nödvändiga egenskaper och metoder för ett specifikt gränssnitt. Det är ett sätt att säkerställa att en variabel passar in i en typdefinition. Här är ett exempel: ```typescript type Columns = 'name' | 'nickName' | 'attributes'; type User = Record; // Type Annotation using `User` const user: User = { name: 'Simone', nickName: undefined, attributes: ['dev', 'admin'], }; // In the following lines, TypeScript won't be able to infer properly user.attributes?.map(console.log); // Property 'map' does not exist on type 'string | string[]'. Property 'map' does not exist on type 'string'. user.nickName; // string | string[] | undefined // Type assertion using `as` const user2 = { name: 'Simon', nickName: undefined, attributes: ['dev', 'admin'], } as User; // Here too, TypeScript won't be able to infer properly user2.attributes?.map(console.log); // Property 'map' does not exist on type 'string | string[]'. Property 'map' does not exist on type 'string'. user2.nickName; // string | string[] | undefined // Using `satisfies` operators we can properly infer the types now const user3 = { name: 'Simon', nickName: undefined, attributes: ['dev', 'admin'], } satisfies User; user3.attributes?.map(console.log); // TypeScript infers correctly: string[] user3.nickName; // TypeScript infers correctly: undefined ``` ### Importer och exporter av enbart typer Importer och exporter av enbart typer låter dig importera eller exportera typer utan att importera eller exportera de värden eller funktioner som är associerade med dessa typer. Detta kan vara användbart för att minska storleken på ditt paket. För att använda importer av enbart typer kan du använda nyckelordet `import type`. TypeScript tillåter användning av både deklarations- och implementationsfiländelser (.ts, .mts, .cts och .tsx) i importer av enbart typer, oavsett inställningarna för `allowImportingTsExtensions`. Till exempel: ```typescript import type { House } from './house.ts'; ``` Följande former stöds: ```typescript import type T from './mod'; import type { A, B } from './mod'; import type * as Types from './mod'; export type { T }; export type { T } from './mod'; ``` ### using-deklaration och explicit resurshantering En `using`-deklaration är en blockomfattande, oföränderlig bindning, liknande `const`, som används för att hantera disponibla resurser. När den initialiseras med ett värde registreras värdets `Symbol.dispose`-metod och körs sedan vid utgång ur det omslutande blockomfånget. Detta baseras på ECMAScripts resurshanteringsfunktion, som är användbar för att utföra väsentliga uppstädningsuppgifter efter objektskapande, såsom att stänga anslutningar, radera filer och frigöra minne. Noteringar: * På grund av dess nyliga introduktion i TypeScript version 5.2 saknar de flesta körmiljöer inbyggt stöd. Du behöver polyfills för: `Symbol.dispose`, `Symbol.asyncDispose`, `DisposableStack`, `AsyncDisposableStack`, `SuppressedError`. * Dessutom behöver du konfigurera din tsconfig.json enligt följande: ```json { "compilerOptions": { "target": "es2022", "lib": ["es2022", "esnext.disposable", "dom"] } } ``` Exempel: ```typescript //@ts-ignore Symbol.dispose ??= Symbol('Symbol.dispose'); // Simple polify const doWork = (): Disposable => { return { [Symbol.dispose]: () => { console.log('disposed'); }, }; }; console.log(1); { using work = doWork(); // Resource is declared console.log(2); } // Resource is disposed (e.g., `work[Symbol.dispose]()` is evaluated) console.log(3); ``` Koden loggar: ```shell 1 2 disposed 3 ``` En resurs som kan disponeras måste följa `Disposable`-gränssnittet: ```typescript // lib.esnext.disposable.d.ts interface Disposable { [Symbol.dispose](): void; } ``` `using`-deklarationer registrerar resursavyttringsoperationer i en stack, vilket säkerställer att de disponeras i omvänd ordning mot deklarationen: ```typescript { using j = getA(), y = getB(); using k = getC(); } // disposes `C`, then `B`, then `A`. ``` Resurser garanteras att disponeras, även om efterföljande kod eller undantag uppstår. Detta kan leda till att avyttringen potentiellt kastar ett undantag, som eventuellt undertrycker ett annat. För att behålla information om undertryckta fel introduceras ett nytt inbyggt undantag, `SuppressedError`. #### await using-deklaration En `await using`-deklaration hanterar en asynkront disponibel resurs. Värdet måste ha en `Symbol.asyncDispose`-metod, som kommer att inväntas vid blockets slut. ```typescript async function doWorkAsync() { await using work = doWorkAsync(); // Resource is declared } // Resource is disposed (e.g., `await work[Symbol.asyncDispose]()` is evaluated) ``` För en asynkront disponibel resurs måste den följa antingen `Disposable`- eller `AsyncDisposable`-gränssnittet: ```typescript // lib.esnext.disposable.d.ts interface AsyncDisposable { [Symbol.asyncDispose](): Promise; } ``` ```typescript //@ts-ignore Symbol.asyncDispose ??= Symbol('Symbol.asyncDispose'); // Simple polify class DatabaseConnection implements AsyncDisposable { // A method that is called when the object is disposed asynchronously [Symbol.asyncDispose]() { // Close the connection and return a promise return this.close(); } async close() { console.log('Closing the connection...'); await new Promise(resolve => setTimeout(resolve, 1000)); console.log('Connection closed.'); } } async function doWork() { // Create a new connection and dispose it asynchronously when it goes out of scope await using connection = new DatabaseConnection(); // Resource is declared console.log('Doing some work...'); } // Resource is disposed (e.g., `await connection[Symbol.asyncDispose]()` is evaluated) doWork(); ``` Koden loggar: ```shell Doing some work... Closing the connection... Connection closed. ``` Deklarationerna `using` och `await using` är tillåtna i följande satser: `for`, `for-in`, `for-of`, `for-await-of`, `switch`. ### Importattribut TypeScript 5.3:s importattribut (etiketter för importer) talar om för körmiljön hur moduler (JSON, etc.) ska hanteras. Detta förbättrar säkerheten genom att säkerställa tydliga importer och överensstämmer med Content Security Policy (CSP) för säkrare resursladdning. TypeScript säkerställer att de är giltiga men låter körmiljön hantera deras tolkning för specifik modulhantering. Exempel: ```typescript import config from './config.json' with { type: 'json' }; ``` med dynamisk import: ```typescript const config = import('./config.json', { with: { type: 'json' } }); ``` ================================================ FILE: README-zh_CN.md ================================================ # 简洁的TypeScript之书 《Concise TypeScript Book》全面而简洁地概述了 TypeScript 的功能。它提供了清晰的解释,涵盖了该语言最新版本中的所有方面,从强大的类型系统到高级功能。无论您是初学者还是经验丰富的开发人员,本书都是增强您对 TypeScript 的理解和熟练程度的宝贵资源。 本书完全免费且开源。 我相信高质量的技术教育应该惠及所有人,因此我坚持本书免费开源。 如果本书帮助您解决了 bug、理解了棘手的概念或提升了您的职业发展,请考虑以您认为合适的金额(建议价格:15 美元)或请我喝杯咖啡来支持我的工作。您的支持将帮助我保持内容更新,并添加新的示例和更深入的解释。 [![Buy Me a Coffee](https://img.shields.io/badge/buy_me_a_coffee-FFDD00?style=for-the-badge&logo=buy-me-a-coffee&logoColor=black)](https://www.buymeacoffee.com/simonepoggiali) [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/donate/?business=QW82ZS956XLFY&no_recurring=0¤cy_code=EUR) ## 翻译 本书已被翻译成多种语言版本,包括: [中文](https://github.com/gibbok/typescript-book/blob/main/README-zh_CN.md) [意大利](https://github.com/gibbok/typescript-book/blob/main/README-it_IT.md) [葡萄牙语(巴西)](https://github.com/gibbok/typescript-book/blob/main/README-pt_BR.md) [瑞典语](https://github.com/gibbok/typescript-book/blob/main/README-sv_SE.md) ## 下载和网站 您还可以下载 Epub 版本: [https://github.com/gibbok/typescript-book/tree/main/downloads](https://github.com/gibbok/typescript-book/tree/main/downloads) 在线版本可在以下位置获得: [https://gibbok.github.io/typescript-book](https://gibbok.github.io/typescript-book) ## 目录表 - [简洁的TypeScript之书](#简洁的typescript之书) - [翻译](#翻译) - [下载和网站](#下载和网站) - [目录表](#目录表) - [介绍](#介绍) - [关于作者](#关于作者) - [TypeScript简介](#typescript简介) - [什么是TypeScript?](#什么是typescript) - [为什么选择 TypeScript?](#为什么选择-typescript) - [TypeScript 和 JavaScript](#typescript-和-javascript) - [TypeScript 代码生成](#typescript-代码生成) - [现在的现代 JavaScript(降级)](#现在的现代-javascript降级) - [TypeScript 入门](#typescript-入门) - [安装](#安装) - [配置](#配置) - [TypeScript 的配置文件](#typescript-的配置文件) - [target](#target) - [lib](#lib) - [strict](#strict) - [module](#module) - [moduleResolution](#moduleresolution) - [esModuleInterop](#esmoduleinterop) - [jsx](#jsx) - [skipLibCheck](#skiplibcheck) - [files](#files) - [include](#include) - [exclude](#exclude) - [importHelpers](#importhelpers) - [迁移到 TypeScript 的建议](#迁移到-typescript-的建议) - [探索类型系统](#探索类型系统) - [TypeScript 的语言服务](#typescript-的语言服务) - [结构类型](#结构类型) - [TypeScript 的基本比较规则](#typescript-的基本比较规则) - [类型作为集合](#类型作为集合) - [赋值类型:类型声明和类型断言](#赋值类型类型声明和类型断言) - [类型声明](#类型声明) - [类型断言](#类型断言) - [非空断言](#非空断言) - [环境声明](#环境声明) - [属性检测和多余属性检测](#属性检测和多余属性检测) - [弱类型](#弱类型) - [严格的对象字面量检测 (Freshness)](#严格的对象字面量检测-freshness) - [类型推断](#类型推断) - [更高级的推断](#更高级的推断) - [类型加宽](#类型加宽) - [常量](#常量) - [类型参数的 const 修饰符](#类型参数的-const-修饰符) - [常量断言](#常量断言) - [显式类型注释](#显式类型注释) - [类型缩小](#类型缩小) - [条件](#条件) - [抛错或者返回](#抛错或者返回) - [可区分联合](#可区分联合) - [用户定义的类型保护](#用户定义的类型保护) - [原始类型](#原始类型) - [string](#string) - [boolean](#boolean) - [number](#number) - [bigInt](#bigint) - [symbol](#symbol) - [null and undefined](#null-and-undefined) - [Array](#array) - [any](#any) - [类型注释](#类型注释) - [可选属性](#可选属性) - [只读属性](#只读属性) - [索引签名](#索引签名) - [扩展类型](#扩展类型) - [字面量类型](#字面量类型) - [字面量推断](#字面量推断) - [严格空检查](#严格空检查) - [枚举](#枚举) - [数字枚举](#数字枚举) - [字符串枚举](#字符串枚举) - [常量枚举](#常量枚举) - [反向映射](#反向映射) - [环境枚举](#环境枚举) - [计算成员和常量成员](#计算成员和常量成员) - [缩小范围](#缩小范围) - [typeof 类型保护](#typeof-类型保护) - [真实性缩小](#真实性缩小) - [相等缩小](#相等缩小) - [In运算符缩小](#in运算符缩小) - [instanceof 缩小](#instanceof-缩小) - [赋值](#赋值) - [控制流分析](#控制流分析) - [类型谓词](#类型谓词) - [可区分联合](#可区分联合-1) - [never 类型](#never-类型) - [详尽性检查](#详尽性检查) - [对象类型](#对象类型) - [元组类型(匿名)](#元组类型匿名) - [命名元组类型(已标记)](#命名元组类型已标记) - [固定长度元组](#固定长度元组) - [联合类型](#联合类型) - [交集类型](#交集类型) - [类型索引](#类型索引) - [值的类型](#值的类型) - [Func 返回值的类型](#func-返回值的类型) - [模块的类型](#模块的类型) - [映射类型](#映射类型) - [映射类型修饰符](#映射类型修饰符) - [条件类型](#条件类型) - [分配条件类型](#分配条件类型) - [infer 条件类型中的类型推断](#infer-条件类型中的类型推断) - [预定义条件类型](#预定义条件类型) - [模板联合类型](#模板联合类型) - [任意类型](#任意类型) - [未知类型](#未知类型) - [空类型](#空类型) - [Never类型](#never类型) - [接口及类型](#接口及类型) - [通用语法](#通用语法) - [基本类型](#基本类型) - [对象和接口](#对象和接口) - [并集和交集类型](#并集和交集类型) - [内置原始数据类型](#内置原始数据类型) - [常见的内置JS对象](#常见的内置js对象) - [重载](#重载) - [合并与扩展](#合并与扩展) - [类型和接口之间的差异](#类型和接口之间的差异) - [Class](#class) - [通用语法](#通用语法-1) - [构造函数](#构造函数) - [私有和受保护的构造函数](#私有和受保护的构造函数) - [访问修饰符](#访问修饰符) - [Get 与 Set](#get-与-set) - [类中的自动访问器](#类中的自动访问器) - [this](#this) - [参数属性](#参数属性) - [抽象类](#抽象类) - [使用泛型](#使用泛型) - [装饰器](#装饰器) - [类装饰器](#类装饰器) - [属性装饰器](#属性装饰器) - [方法装饰器](#方法装饰器) - [Getter 和 Setter 装饰器](#getter-和-setter-装饰器) - [装饰器元数据](#装饰器元数据) - [继承](#继承) - [静态成员](#静态成员) - [属性初始化](#属性初始化) - [方法重载](#方法重载) - [泛型](#泛型) - [泛型类型](#泛型类型) - [泛型类](#泛型类) - [泛型约束](#泛型约束) - [泛型上下文缩小](#泛型上下文缩小) - [擦除的结构类型](#擦除的结构类型) - [命名空间](#命名空间) - [Symbols](#symbols) - [三斜杠指令](#三斜杠指令) - [类型操作](#类型操作) - [从类型创建类型](#从类型创建类型) - [索引访问类型](#索引访问类型) - [工具类型](#工具类型) - [Awaited\](#awaitedt) - [Partial\](#partialt) - [Required\](#requiredt) - [Readonly\](#readonlyt) - [Record\](#recordk-t) - [Pick\](#pickt-k) - [Omit\](#omitt-k) - [Exclude\](#excludet-u) - [Extract\](#extractt-u) - [NonNullable\](#nonnullablet) - [Parameters\](#parameterst) - [ConstructorParameters\](#constructorparameterst) - [ReturnType\](#returntypet) - [InstanceType\](#instancetypet) - [ThisParameterType\](#thisparametertypet) - [OmitThisParameter\](#omitthisparametert) - [ThisType\](#thistypet) - [Uppercase\](#uppercaset) - [Lowercase\](#lowercaset) - [Capitalize\](#capitalizet) - [Uncapitalize\](#uncapitalizet) - [NoInfer\](#noinfert) - [其他](#其他) - [错误和异常处理](#错误和异常处理) - [混合类](#混合类) - [异步语言特性](#异步语言特性) - [迭代器和生成器](#迭代器和生成器) - [TsDocs JSDoc 参考](#tsdocs-jsdoc-参考) - [@types](#types) - [JSX](#jsx-1) - [ES6 模块](#es6-模块) - [ES7 求幂运算符](#es7-求幂运算符) - [for-await-of 语句](#for-await-of-语句) - [New target 元属性](#new-target-元属性) - [动态导入表达式](#动态导入表达式) - ["tsc –watch"](#tsc-watch) - [默认声明](#默认声明) - [可选链](#可选链) - [空合并运算符](#空合并运算符) - [模板字符串类型](#模板字符串类型) - [函数重载](#函数重载) - [递归类型](#递归类型) - [递归条件类型](#递归条件类型) - [Node 中的 ECMAScript 模块支持](#node-中的-ecmascript-模块支持) - [断言函数](#断言函数) - [可变参数元组类型](#可变参数元组类型) - [装箱类型](#装箱类型) - [TypeScript 中的协变和逆变](#typescript-中的协变和逆变) - [类型参数的可选方差注释](#类型参数的可选方差注释) - [模板字符串模式索引签名](#模板字符串模式索引签名) - [satisfies操作符](#satisfies操作符) - [仅类型导入和导出](#仅类型导入和导出) - [使用声明和显式资源管理](#使用声明和显式资源管理) - [使用声明等待](#使用声明等待) - [导入属性](#导入属性) ## 介绍 欢迎来到简洁的TypeScript之书!本指南为您提供有效 TypeScript 开发的基本知识和实践技能。发现编写干净、健壮的代码的关键概念和技术。无论您是初学者还是经验丰富的开发人员,本书都可以作为在项目中利用 TypeScript 强大功能的综合指南和便捷参考。 本书涵盖了 TypeScript 5.2。 ## 关于作者 Simone Poggiali 是一位经验丰富的资深工程师,自 90 年代以来便对编写专业级代码充满热情。在他的国际职业生涯中,他曾为众多客户(从初创公司到大型企业)参与过各种项目。HelloFresh、西门子、O2、Leroy Merlin 和 Snowplow 等知名公司都曾受益于他的专业知识和敬业精神。 您可以通过以下平台联系 Simone Poggiali: * 领英: [https://www.linkedin.com/in/simone-poggiali](https://www.linkedin.com/in/simone-poggiali) * GitHub: [https://github.com/gibbok](https://github.com/gibbok) * X.com: [https://x.com/gibbok_coding](https://x.com/gibbok_coding) * 电子邮箱: gibbok.coding📧gmail.com 完整的贡献者名单:[https://github.com/gibbok/typescript-book/graphs/contributors](https://github.com/gibbok/typescript-book/graphs/contributors) ## TypeScript简介 ### 什么是TypeScript? TypeScript 是一种基于 JavaScript 的强类型编程语言。它最初由 Anders Hejlsberg 于 2012 年设计,目前由微软开发和维护,是一个开源项目。 TypeScript 会被编译成 JavaScript,并可在任何 JavaScript 运行时环境(例如浏览器或服务器上的 Node.js)中执行。 它支持多种编程范式,例如函数式编程、泛型编程、命令式编程和面向对象编程。TypeScript 是一种编译型(转译型)语言,在执行前会被转换为 JavaScript。 ### 为什么选择 TypeScript? TypeScript 是一种强类型语言,有助于防止常见的编程错误,并在程序执行之前避免某些类型的运行时错误。 强类型语言允许开发人员在数据类型定义中指定各种程序约束和行为,从而有助于验证软件的正确性并防止缺陷。这在大规模应用中尤其有价值。 TypeScript 的一些好处 * 静态类型,可选强类型 * 类型推断 * 能使用ES6和ES7的新功能 * 跨平台和跨浏览器兼容性 \* IntelliSense 工具支持 ### TypeScript 和 JavaScript TypeScript是用`.ts`或`.tsx`文件编写的, 而JavaScript是用`.js`或`.jsx`文件编写的。 扩展名为.tsx或.jsx的文件可以包含 JavaScript 语法扩展 JSX,该扩展在 React 中用于 UI 开发。 就语法而言,TypeScript 是 JavaScript (ECMAScript 2015) 的类型化超集。所有 JavaScript 代码都是有效的 TypeScript 代码,但反之则不然。 例如,考虑 JavaScript 文件中具有.js扩展名的函数,如下所示: ```typescript const sum = (a, b) => a + b; ``` 该函数可以通过将文件扩展名更改为 .TypeScript 来转换和使用.ts。但是,如果同一个函数使用 TypeScript 类型进行注释,则未经编译就无法在任何 JavaScript 运行时中执行。如果未编译以下 TypeScript 代码,将会产生语法错误 ```typescript const sum = (a: number, b: number): number => a + b; ``` TypeScript 旨在通过让开发人员使用类型注释定义意图来检测编译期间运行时可能发生的异常。此外,如果没有提供类型注释,TypeScript 也可以捕获问题。例如,以下代码片段未指定任何 TypeScript 类型: ```typescript const items = [{ x: 1 }, { x: 2 }]; const result = items.filter(item => item.y); ``` 在这种情况下,TypeScript 检测到错误并报告: ```text 类型 '{ x: number; }' 上不存在属性 'y' 。 ``` TypeScript 的类型系统很大程度上受到 JavaScript 运行时行为的影响。例如,加法运算符 (+) 在 JavaScript 中可以执行字符串连接或数字加法,在 TypeScript 中以相同的方式建模: ```typescript const result = '1' + 1; // 结果是string类型 ``` TypeScript 背后的团队经过深思熟虑,决定将 JavaScript 的异常使用标记为错误。例如,考虑以下有效的 JavaScript 代码: ```typescript const result = 1 + true; // 在JavaScript中, 结果等于2 ``` 但是,TypeScript 会抛出错误: ```text 运算符"+"不能应用于类型"number"和"boolean"。 `````` 出现此错误的原因是 TypeScript 严格强制执行类型兼容性,在这种情况下,它标识了数字和布尔值之间的无效操作。 ### TypeScript 代码生成 TypeScript 编译器有两个主要职责:检查类型错误和编译为 JavaScript。这两个过程是相互独立的。类型不会影响 JavaScript 运行时中代码的执行,因为它们在编译过程中会被完全擦除。即使存在类型错误,TypeScript 仍然可以输出 JavaScript。以下是存在类型错误的 TypeScript 代码示例: ```typescript const add = (a: number, b: number): number => a + b; const result = add('x', 'y'); // "字符串"类型的参数不可赋值给"数字"类型的参数 ``` 但是,它仍然可以生成可执行的 JavaScript 输出: ```typescript 'use strict'; const add = (a, b) => a + b; const result = add('x', 'y'); // xy ``` 无法在运行时检查 TypeScript 类型。例如: ```typescript interface Animal { name: string; } interface Dog extends Animal { bark: () => void; } interface Cat extends Animal { meow: () => void; } const makeNoise = (animal: Animal) => { if (animal instanceof Dog) { // "Dog"仅指一种类型,但在这里用作值。 // ... } }; ``` 由于编译后类型被删除,因此无法在 JavaScript 中运行此代码。为了在运行时识别类型,我们需要使用另一种机制。TypeScript 提供了多种选项,其中常见的一个是 "标签联合(tagged union)"。例如: ```typescript interface Dog { kind: 'dog'; // 标签联合 bark: () => void; } interface Cat { kind: 'cat'; // 标签联合 meow: () => void; } type Animal = Dog | Cat; const makeNoise = (animal: Animal) => { if (animal.kind === 'dog') { animal.bark(); } else { animal.meow(); } }; const dog: Dog = { kind: 'dog', bark: () => console.log('bark'), }; makeNoise(dog); ``` 属性"kind"是一个可以在运行时用来区分 JavaScript 中的对象的值。 运行时的值也可能具有与类型声明中声明的类型不同的类型。例如,如果开发人员误解了 API 类型并对其进行了错误注释。 TypeScript 是 JavaScript 的超集,因此"class"关键字可以在运行时用作类型和值。 ```typescript class Animal { constructor(public name: string) {} } class Dog extends Animal { constructor( public name: string, public bark: () => void ) { super(name); } } class Cat extends Animal { constructor( public name: string, public meow: () => void ) { super(name); } } type Mammal = Dog | Cat; const makeNoise = (mammal: Mammal) => { if (mammal instanceof Dog) { mammal.bark(); } else { mammal.meow(); } }; const dog = new Dog('Fido', () => console.log('bark')); makeNoise(dog); ``` 在 JavaScript 中,"类"具有"prototype"属性,"instanceof"运算符可用于测试构造函数的原型属性是否出现在对象原型链中的任何位置。 TypeScript 对运行时性能没有影响,因为所有类型都将被删除。然而,TypeScript 确实引入了一些构建时间开销。 ### 现在的现代 JavaScript(降级) TypeScript 可以将代码编译为自 ECMAScript 3 (1999) 以来任何已发布的 JavaScript 版本。这意味着 TypeScript 可以将代码从最新的 JavaScript 功能转换为旧版本,这一过程称为降级。这允许使用现代 JavaScript,同时保持与旧运行时环境的最大兼容性。 值得注意的是,在转换为旧版本 JavaScript 的过程中,TypeScript 可能会生成与本机实现相比会产生性能开销的代码。 以下是一些可以在 TypeScript 中使用的现代 JavaScript 功能: * ECMAScript 模块,而不是 AMD 风格的"define"回调或 CommonJS 的"require"语句。 * 用类代替原型。 * 变量声明使用"let"或"const"而不是"var"。 * "for-of"循环或".forEach"而不是传统的"for"循环。 * 用箭头函数代替函数表达式。 * 解构赋值。 * 简写属性/方法名称和计算属性名称。 * 默认函数参数。 通过利用这些现代 JavaScript 功能,开发人员可以在 TypeScript 中编写更具表现力和简洁的代码。 ## TypeScript 入门 ### 安装 Visual Studio Code 为 TypeScript 语言提供了出色的支持,但不包含 TypeScript 编译器。要安装 TypeScript 编译器,您可以使用包管理器,例如 npm 或yarn: ```shell npm install typescript --save-dev ``` 或者 ```shell yarn add typescript --dev ``` 确保提交生成的锁定文件,以确保每个团队成员使用相同版本的 TypeScript。 要运行TypeScript编译器,可以使用以下命令 ```shell npx tsc ``` 或者 ```shell yarn tsc ``` 建议按项目安装 TypeScript,而不是全局安装,因为它提供了更可预测的构建过程。但是,对于一次性情况,您可以使用以下命令: ```shell npx tsc ``` 或者安装到全局: ```shell npm install -g typescript ``` 如果您使用的是 Microsoft Visual Studio,则可以在 NuGet 中为 MSBuild 项目获取作为包的 TypeScript。在 NuGet 包管理器控制台中,运行以下命令: ```shell Install-Package Microsoft.TypeScript.MSBuild ``` 在 TypeScript 安装过程中,会安装两个可执行文件:"tsc"作为 TypeScript 编译器,"tsserver"作为 TypeScript 独立服务器。独立服务器包含编译器和语言服务,编辑器和 IDE 可以利用它们来提供智能代码补全。 此外,还有几种兼容 TypeScript 的转译器可用,例如 Babel(通过插件)或 swc。这些转译器可用于将 TypeScript 代码转换为其他目标语言或版本。 ### 配置 可以使用 tsc CLI 选项或利用位于项目根目录中名为 tsconfig.json 的专用配置文件来配置 TypeScript。 要生成预填充推荐设置的 tsconfig.json 文件,您可以使用以下命令: ```shell tsc --init ``` 在本地执行tsc命令时,TypeScript 将使用最近的 tsconfig.json 文件中指定的配置来编译代码。 以下是使用默认设置运行的 CLI 命令的一些示例: ```shell tsc main.ts // 将一个特定的文件 (main.ts) 编译成 JavaScript tsc src/*.ts // 将 'src' 文件夹下任意的 .ts 文件编译成 JavaScript tsc app.ts util.ts --outfile index.js // 将 2 个 TypeScript 文件 (app.ts 和 util.ts) 编译成 1 个 JavaScript 文件 (index.js) ``` ### TypeScript 的配置文件 tsconfig.json 文件用于配置 TypeScript 编译器 (tsc)。通常,它与文件一起添加到项目的根目录中package.json。 注意: * tsconfig.json 即使是 json 格式也接受注释。 * 建议使用此配置文件而不是命令行选项。 在以下链接中,您可以找到完整的文档及其配置示例: [https://www.typescriptlang.org/tsconfig](https://www.typescriptlang.org/tsconfig) [https://www.typescriptlang.org/tsconfig/](https://www.typescriptlang.org/tsconfig/) 以下列出了常见且有用的配置: #### target "target"属性用于指定 TypeScript 应发出/编译到哪个版本的 JavaScript ECMAScript 版本。对于现代浏览器,ES6是一个不错的选择,对于较旧的浏览器,建议使用ES5。 #### lib "lib"属性用于指定编译时要包含哪些库文件。TypeScript 自动包含"目标"属性中指定功能的 API,但可以根据特定需求省略或选择特定库。例如,如果您正在开发服务器项目,则可以排除"DOM"库,该库仅在浏览器环境中有用。 #### strict "strict"属性可以提供更强有力的保证并增强类型安全性。建议始终将此属性包含在项目的 tsconfig.json 文件中。启用"strict"属性允许 TypeScript : * 触发每个源文件的代码使用"use strict"。 * 在类型检查过程中考虑"null"和"undefined" * 当不存在类型注释时禁用"any"类型的使用。 * 在使用"this"表达式时引发错误,否则"this"会被视为任意类型。 #### module "module"属性设置编译程序支持的模块系统。在运行时,模块加载器用于根据指定的模块系统定位并执行依赖项。 JavaScript 中最常见的模块加载器是用于服务器端应用程序的 Node.js 的CommonJS和用于基于浏览器的 Web 应用程序中的 AMD 模块的 RequireJS。 TypeScript 可以为各种模块系统生成代码,包括 UMD、System、ESNext、ES2015/ES6 和 ES2020。 注意:应根据目标环境和该环境中可用的模块加载机制来选择模块系统。 #### moduleResolution "moduleResolution"属性指定模块解析策略。对现代TypeScript代码使用"node","classic"仅用于旧版本的 TypeScript(1.6 之前)。 #### esModuleInterop "esModuleInterop"属性允许从未使用"default"属性导出的 CommonJS 模块导入默认值,此属性提供了一个兼容以确保生成的 JavaScript 的兼容性。启用此选项后,我们可以使用 `import MyLibrary from "my-library"` 而不是 `import * as MyLibrary from "my-library"`。 #### jsx "jsx"属性仅适用于 ReactJS 中使用的 .tsx 文件,并控制 JSX 构造如何编译为 JavaScript。一个常见的选项是"preserve",它将编译为 .jsx 文件,保持 JSX 不变,以便可以将其传递给 Babel 等不同工具进行进一步转换。 #### skipLibCheck "skipLibCheck"属性将阻止 TypeScript 对整个导入的第三方包进行类型检查。此属性将减少项目的编译时间。TypeScript 仍会根据这些包提供的类型定义检查您的代码。 #### files "files"属性向编译器指示必须始终包含在程序中的文件列表。 #### include "include"属性向编译器指示我们想要包含的文件列表。此属性允许类似 glob 的模式,例如 "\*_" 表示任何子目录,"_" 表示任何文件名,"?" 表示可选字符。 #### exclude "exclude"属性向编译器指示不应包含在编译中的文件列表。这可以包括"node_modules"等文件或测试文件 注意:tsconfig.json 允许注释。 ### importHelpers TypeScript 在为某些高级或低级 JavaScript 功能生成代码时使用帮助程序代码。 默认情况下,这些助手会在使用它们的文件中复制。 `importHelpers` 选项从 `tslib` 模块导入这些帮助器,从而使 JavaScript 输出更加高效。 ### 迁移到 TypeScript 的建议 对于大型项目,建议采用逐渐过渡的方式,其中 TypeScript 和 JavaScript 代码最初共存。只有小型项目才能一次性迁移到 TypeScript。 此转变的第一步是将 TypeScript 引入构建链过程。这可以通过使用"allowJs"编译器选项来完成,该选项允许 .ts 和 .tsx 文件与现有 JavaScript 文件共存。由于当 TypeScript 无法从 JavaScript 文件推断类型时,它会回退到变量的"any"类型,因此建议在迁移开始时在编译器选项中禁用"noImplicitAny"。 第二步是确保您的 JavaScript 测试与 TypeScript 文件一起工作,以便您可以在转换每个模块时运行测试。如果您正在使用 Jest,请考虑使用ts-jest,它允许您使用 Jest 测试 TypeScript 项目。 第三步是在项目中包含第三方库的类型声明。 这些声明可以第三方库的类型声明文件或专门的声明包中找到,你能通过 [https://www.typescriptlang.org/dt/search](https://www.typescriptlang.org/dt/search) 搜索并安装它们。: ```shell npm install --save-dev @types/package-name ``` 或者 ```shell yarn add --dev @types/package-name ``` 第四步是使用自下而上的方法逐个模块地迁移,遵循从叶开始的依赖关系图。这个想法是开始转换不依赖于其他模块的模块。要可视化依赖关系图,您可以使用该madge工具。 有一些对于转换成 TypeScript 比较友好的模块(外部 API 或规范相关的实用函数和代码),比如Swagger、GraphQL 或 JSONSchema 自动生成 TypeScript 类型定义,并使用在您的项目中。 当没有可用的规范或官方架构时,您可以从原始数据生成类型,例如服务器返回的 JSON。但是,建议从规范而不是数据生成类型,以避免丢失边缘情况。 在迁移过程中,不要进行代码重构,而只专注于向模块添加类型。 第五步是启用"noImplicitAny",这将强制所有类型都是已知和定义的,从而为您的项目提供更好的 TypeScript 体验。 在迁移过程中,您可以使用该@ts-check指令,该指令在 JavaScript 文件中启用 TypeScript 类型检查。该指令提供了宽松版本的类型检查,最初可用于识别 JavaScript 文件中的问题。当@ts-check包含在文件中时,TypeScript 将尝试使用 JSDoc 风格的注释来推断定义。但是,仅在迁移的早期阶段考虑使用 JSDoc 注释。 考虑在你的tsconfig.json文件中将 `noEmitOnError` 设置为 false,即使报告错误,这也将允许您输出 JavaScript 源代码。 ## 探索类型系统 ### TypeScript 的语言服务 TypeScript 的语言服务, 也被称为 tsserver,提供了各种功能,例如错误报告、诊断、保存时编译、重命名、跳转到定义、补全列表、签名帮助等。 它主要由集成开发环境 (IDE) 使用来提供 IntelliSense 支持。它与 Visual Studio Code 无缝集成,并由 Conquer of Completion (Coc) 等工具使用。 开发人员可以利用专用 API 并创建自己的自定义语言服务插件来增强 TypeScript 编辑体验。这对于实现特殊的 linting 功能或启用自定义模板语言的自动完成特别有用。 现实世界中的自定义插件的一个示例是"typescript-styled-plugin",它为样式组件中的 CSS 属性提供语法错误报告和 IntelliSense 支持。 有关更多信息和快速入门指南,您可以参考 GitHub 上的官方 TypeScript Wiki: [https://github.com/microsoft/TypeScript/wiki/](https://github.com/microsoft/TypeScript/wiki/) ### 结构类型 TypeScript 基于结构类型系统。这意味着类型的兼容性和等效性由类型的实际结构或定义决定,而不是由其名称或声明位置决定,如 C# 或 C 等主要类型系统中那样。 TypeScript 的结构类型系统是基于 JavaScript 的动态 duck 类型系统在运行时的工作方式而设计的。 以下示例是有效的 TypeScript 代码。正如您所观察到的,"X"和"Y"具有相同的成员"a",尽管它们具有不同的声明名称。类型由其结构决定,在这种情况下,由于结构相同,因此它们是兼容且有效的。 ```typescript type X = { a: string; }; type Y = { a: string; }; const x: X = { a: 'a' }; const y: Y = x; // 有效 ``` ### TypeScript 的基本比较规则 TypeScript 比较过程是递归的,并在任何级别嵌套的类型上执行。 如果"Y"至少具有与"X"相同的成员,则类型"X"与"Y"兼容。 ```typescript type X = { a: string; }; const y = { a: 'A', b: 'B' }; // 有效, 至少它拥有相同的成员 X const r: X = y; ``` 函数参数按类型进行比较,而不是按名称进行比较: ```typescript type X = (a: number) => void; type Y = (a: number) => void; let x: X = (j: number) => undefined; let y: Y = (k: number) => undefined; y = x; // 有效 x = y; // 有效 ``` 函数返回类型必须相同: ```typescript type X = (a: number) => undefined; type Y = (a: number) => number; let x: X = (a: number) => undefined; let y: Y = (a: number) => 1; y = x; // 无效 x = y; // 无效 ``` 源函数的返回类型必须是目标函数的返回类型的子类型: ```typescript let x = () => ({ a: 'A' }); let y = () => ({ a: 'A', b: 'B' }); x = y; // 有效 y = x; // 无效,缺少 b 成员 ``` 允许丢弃函数参数,因为这是 JavaScript 中的常见做法,例如使用 "Array.prototype.map()": ```typescript [1, 2, 3].map((element, _index, _array) => element + 'x'); ``` 因此,以下类型声明是完全有效的: ```typescript type X = (a: number) => undefined; type Y = (a: number, b: number) => undefined; let x: X = (a: number) => undefined; let y: Y = (a: number) => undefined; // 缺少 b 参数 y = x; // 有效 ``` 源类型的任何附加可选参数都是有效的: ```typescript type X = (a: number, b?: number, c?: number) => undefined; type Y = (a: number) => undefined; let x: X = a => undefined; let y: Y = a => undefined; y = x; // 有效 x = y; // 有效 ``` 目标类型的任何可选参数在源类型中没有对应的参数都是有效的并且不是错误: ```typescript type X = (a: number) => undefined; type Y = (a: number, b?: number) => undefined; let x: X = a => undefined; let y: Y = a => undefined; y = x; // 有效 x = y; // 有效 ``` 其余参数被视为无限系列的可选参数: ```typescript type X = (a: number, ...rest: number[]) => undefined; let x: X = a => undefined; // 有效 ``` 如果重载签名与其实现签名兼容,则具有重载的函数有效: ```typescript function x(a: string): void; function x(a: string, b: number): void; function x(a: string, b?: number): void { console.log(a, b); } x('a'); // 有效 x('a', 1); // 有效 function y(a: string): void; // 无效, 不兼容重载的签名 function y(a: string, b: number): void; function y(a: string, b: number): void { console.log(a, b); } y('a'); y('a', 1); ``` 如果源参数和目标参数可赋值给超类型或子类型(Bivariance 双变),则函数参数比较成功。 ```typescript // 超类 class X { a: string; constructor(value: string) { this.a = value; } } // 子类 class Y extends X {} // 子类 class Z extends X {} type GetA = (x: X) => string; const getA: GetA = x => x.a; // 双变(Bivariance) 确实接收超类 console.log(getA(new X('x'))); // 有效 console.log(getA(new Y('Y'))); // 有效 console.log(getA(new Z('z'))); // 有效 ``` 枚举与数字具有可比性和有效性,反之亦然,但比较不同枚举类型的枚举值是无效的。 ```typescript enum X { A, B, } enum Y { A, B, C, } const xa: number = X.A; // 有效 const ya: Y = 0; // 有效 X.A === Y.A; // 无效 ``` 类的实例需要对其私有成员和受保护成员进行兼容性检查: ```typescript class X { public a: string; constructor(value: string) { this.a = value; } } class Y { private a: string; constructor(value: string) { this.a = value; } } let x: X = new Y('y'); // 无效 ``` 比较检查不考虑不同的继承层次结构,例如: ```typescript class X { public a: string; constructor(value: string) { this.a = value; } } class Y extends X { public a: string; constructor(value: string) { super(value); this.a = value; } } class Z { public a: string; constructor(value: string) { this.a = value; } } let x: X = new X('x'); let y: Y = new Y('y'); let z: Z = new Z('z'); x === y; // 有效 x === z; // 有效即使 z 来自不同的继承层次结构 ``` 泛型根据应用泛型参数后的结果类型使用其结构进行比较,仅将最终结果作为非泛型类型进行比较。 ```typescript interface X { a: T; } let x: X = { a: 1 }; let y: X = { a: 'a' }; x === y; // 无效,因为最终结构中使用了类型参数 ``` ```typescript interface X {} const x: X = 1; const y: X = 'a'; x === y; // 有效,因为最终结构中没有使用类型参数 ``` 当泛型未指定其类型参数时,所有未指定的参数都将被视为带有"any"的类型: ```typescript type X = (x: T) => T; type Y = (y: K) => K; let x: X = x => x; let y: Y = y => y; x = y; // 有效 ``` 记住: ```typescript let a: number = 1; let b: number = 2; a = b; // 有效,一切都可以赋值给自己 let c: any; c = 1; // 有效,所有类型都可以赋值给any let d: unknown; d = 1; // 有效,所有类型都可以赋值给unknown let e: unknown; let e1: unknown = e; // 有效, unknown只能赋值给自己和any let e2: any = e; // 有效 let e3: number = e; // 无效 let f: never; f = 1; // 无效, 所有类型不能赋值给never let g: void; let g1: any; g = 1; // 无效, void不可赋值给除"any"之外的任何内容或从任何内容赋值 g = g1; // 有效 ``` 请注意,当启用"strictNullChecks"时,"null"和"undefined"的处理方式与"void"类似;否则,它们类似于"never"。 ### 类型作为集合 在 TypeScript 中,类型是一组可能的值。该集合也称为类型的域。类型的每个值都可以被视为集合中的一个元素。类型建立了集合中的每个元素必须满足才能被视为该集合的成员的约束。TypeScript 的主要任务是检查并验证一组是否是另一组的子集。 TypeScript 支持各种类型的集合: | Set term | TypeScript | Notes | | -------- | ------------------------------- | ------------------------------------------------------------------------------ | | 空集 | never | "never" 包含除自身之外的任何类型 | | 单元素集 | undefined / null / literal type | | | 有限集 | boolean / union | | | 无限集 | string / number / object | | | 通用集 | any / unknown | 每个元素都是"any"的成员,每个集合都是它的子集/"unknown"是"any"的类型安全对应项 | 这里有几个例子: | TypScript | Set term | Example | | --------------------- | ---------------- | ---------------------------------------------------------------- | | never | ∅ (空集) | const x: never = 'x'; // 错误: 'string'类似不能赋值给'never'类型 | | | | | Literal type | 单元素集 | type X = 'X'; | | | | type Y = 7; | | | | | Value assignable to T | Value ∈ T (属于) | type XY = 'X' \| 'Y'; | | | | const x: XY = 'X'; | | | | | T1 assignable to T2 | T1 ⊆ T2 (子集) | type XY = 'X' \| 'Y'; | | | | const x: XY = 'X'; | | | | const j: XY = 'J'; // 类型'"J"' 不能赋值给 'XY' 类型. | | | | | | T1 extends T2 | T1 ⊆ T2 (子集) | type X = 'X' extends string ? true : false; | | | | | T1 \| T2 | T1 ∪ T2 (并集) | type XY = 'X' \| 'Y'; | | | | type JK = 1 \| 2; | | | | | T1 & T2 | T1 ∩ T2 (交集) | type X = \{ a: string \} | | | | type Y = \{ b: string \} | | | | type XY = X & Y | | | | const x: XY = \{ a: 'a', b: 'b' \} | | | | | unknown | 通用集 | const x: unknown = 1 | 并集 (T1 | T2) 创建一个更广泛的集合(两者): ```typescript type X = { a: string; }; type Y = { b: string; }; type XY = X | Y; const r: XY = { a: 'a', b: 'x' }; // 有效 ``` 交集(T1 & T2)创建一个更窄的集合(仅共享): ```typescript type X = { a: string; }; type Y = { a: string; b: string; }; type XY = X & Y; const r: XY = { a: 'a' }; // 无效 const j: XY = { a: 'a', b: 'b' }; // 有效 ``` 在这种情况下,关键字extends可以被视为"的子集"。它为类型设置约束。与泛型一起使用的扩展将泛型视为无限集,并将其限制为更具体的类型。请注意,这extends与 OOP 意义上的层次结构无关(TypScript 中没有这个概念)。TypeScript 使用集合并且没有严格的层次结构,事实上,如下面的示例所示,两种类型可以重叠,而不会成为另一种类型的子类型(TypScript 考虑对象的结构和形状)。 ```typescript interface X { a: string; } interface Y extends X { b: string; } interface Z extends Y { c: string; } const z: Z = { a: 'a', b: 'b', c: 'c' }; interface X1 { a: string; } interface Y1 { a: string; b: string; } interface Z1 { a: string; b: string; c: string; } const z1: Z1 = { a: 'a', b: 'b', c: 'c' }; const r: Z1 = z; // 有效 ``` ### 赋值类型:类型声明和类型断言 在 TypeScript 中可以通过不同的方式赋值类型: #### 类型声明 在下面的示例中,我们使用 x:X(":Type") 来声明变量 x 的类型。 ```typescript type X = { a: string; }; // 类型声明 const x: X = { a: 'a', }; ``` 如果变量不是指定的格式,TypeScript 将报告错误。例如: ```typescript type X = { a: string; }; const x: X = { a: 'a', b: 'b', // 错误: 对象字面量只能指定已知属性 }; ``` #### 类型断言 可以使用as关键字添加断言。这告诉编译器开发人员拥有有关类型的更多信息并消除可能发生的任何错误。 例如: ```typescript type X = { a: string; }; const x = { a: 'a', b: 'b', } as X; ``` 在上面的示例中,使用 as 关键字将对象 x 断言为类型 X。这通知 TypeScript 编译器该对象符合指定的类型,即使它具有类型定义中不存在的附加属性 b。 类型断言在需要指定更具体类型的情况下非常有用,尤其是在使用 DOM 时。例如: ```typescript const myInput = document.getElementById('my_input') as HTMLInputElement; ``` 此处,类型断言 HTMLInputElement 用于告诉 TypeScript getElementById 的结果应被视为 HTMLInputElement。类型断言还可以用于重新映射键,如下面使用模板文字的示例所示: ```typescript type J = { [Property in keyof Type as `prefix_${string & Property}`]: () => Type[Property]; }; type X = { a: string; b: number; }; type Y = J; ``` 在此示例中,类型 J 使用带有模板文字的映射类型来重新映射 Type 的键。它创建新属性,并在每个键上添加 prefix_ ,它们对应的值是返回原始属性值的函数。 值得注意的是,当使用类型断言时,TypeScript 不会执行多余的属性检查。因此,当预先知道对象的结构时,通常最好使用类型声明。 #### 非空断言 此断言是使用后缀表达式!运算符应用的,它告诉 TypeScript 值不能为 null 或未定义。 ```typescript let x: null | number; let y = x!; // number ``` #### 环境声明 环境声明是描述 JavaScript 代码类型的文件,它们的文件名格式为.d.ts.. 它们通常被导入并用于注释现有的 JavaScript 库或向项目中的现有 JS 文件添加类型。 许多常见的库类型可以在以下位置找到: [https://github.com/DefinitelyTyped/DefinitelyTyped/](https://github.com/DefinitelyTyped/DefinitelyTyped/) ```shell npm install --save-dev @types/library-name ``` 对于您定义的环境声明,您可以使用"三斜杠"引用导入: ```typescript /// ``` 即使在 JavaScript 文件中,您也可以通过 `// @ts-check` 使用环境声明。 `declare` 关键字可以为现有的 JavaScript 代码启用类型定义,而无需导入它,作为来自另一个文件或全局的类型的占位符。 ### 属性检测和多余属性检测 TypeScript 基于结构类型系统,但过多的属性检查是 TypeScript 的一个属性,它允许它检查对象是否具有类型中指定的确切属性。 例如,在将对象字面量赋值给变量或将它们作为参数传递给函数的多余属性时,会执行多余属性检查。 ```typescript type X = { a: string; }; const y = { a: 'a', b: 'b' }; const x: X = y; // 有效,因为结构类型 const w: X = { a: 'a', b: 'b' }; // 无效,因为多余属性检测 ``` ### 弱类型 当一个类型只包含一组全可选属性时,该类型被认为是弱类型: ```typescript type X = { a?: string; b?: string; }; ``` 当没有重叠时,TypeScript 认为将任何内容赋值给弱类型是错误的,例如,以下会引发错误: ```typescript type Options = { a?: string; b?: string; }; const fn = (options: Options) => undefined; fn({ c: 'c' }); // 无效 ``` 尽管不推荐,但如果需要,可以使用类型断言绕过此检查: ```typescript type Options = { a?: string; b?: string; }; const fn = (options: Options) => undefined; fn({ c: 'c' } as Options); // 有效 ``` 或者通过将unknown索引签名添加到弱类型: ```typescript type Options = { [prop: string]: unknown; a?: string; b?: string; }; const fn = (options: Options) => undefined; fn({ c: 'c' }); // 有效 ``` ### 严格的对象字面量检测 (Freshness) 严格的对象字面量检查(有时称为“新鲜度”)是 TypeScript 中的一项功能,有助于捕获多余或拼写错误的属性,否则这些属性在正常结构类型检查中会被忽视。 创建对象字面量时,TypeScript 编译器认为它是“新鲜的”。 如果将对象字面量分配给变量或作为参数传递,并且对象字面量指定目标类型中不存在的属性,则 TypeScript 将引发错误。 然而,当扩展对象文字或使用类型断言时,“新鲜感”就会消失。 下面举一些例子来说明: ```typescript type X = { a: string }; type Y = { a: string; b: string }; let x: X; x = { a: 'a', b: 'b' }; // 严格的对象字面量检查:无效的赋值 var y: Y; y = { a: 'a', bx: 'bx' }; // 严格的对象字面量检查:无效的赋值 const fn = (x: X) => console.log(x.a); fn(x); fn(y); // 类型加宽:没有错误, 结构类型兼容 fn({ a: 'a', bx: 'b' }); // 严格的对象字面量检查:无效的参数 let c: X = { a: 'a' }; let d: Y = { a: 'a', b: '' }; c = d; // 类型加宽:没有严格的对象字面量检查 ``` ### 类型推断 当在以下期间未提供注释时,TypeScript 可以推断类型: * 变量初始化 * 成员初始化。 * 设置参数的默认值。 * 函数返回类型。 例如: ```typescript let x = 'x'; // 推断的类型是 string ``` TypeScript 编译器分析值或表达式并根据可用信息确定其类型。 ### 更高级的推断 当在类型推断中使用多个表达式时,TypeScript 会查找"最佳常见类型"。例如: ```typescript let x = [1, 'x', 1, null]; // 类型推断为: (string | number | null)[] ``` 如果编译器找不到最佳通用类型,它将返回联合类型。例如: ```typescript let x = [new RegExp('x'), new Date()]; // 类型推断为: (RegExp | Date)[] ``` TypeScript 利用基于变量位置的"上下文类型"来推断类型。在下面的示例中,编译器知道它的e类型是MouseEvent,因为在lib.d.ts 文件中定义了click事件类型,该文件包含各种常见 JavaScript 构造和 DOM 的环境声明: ```typescript window.addEventListener('click', function (e) {}); // e 的类型被推断为 MouseEvent ``` ### 类型加宽 类型加宽是 TypeScript 将类型分配给未提供类型注释时初始化的变量的过程。它允许从窄到宽的类型,但反之则不然。在以下示例中: ```typescript let x = 'x'; // TypeScript 推断为字符串,一种宽类型 let y: 'y' | 'x' = 'y'; // y 类型是字面量类型的联合 y = x; // 无效,字符串不可分配给类型 'x' | 'y'。 ``` TypeScript根据初始化期间提供的单个值(`x`),将 `string` 赋予给 `x`,这是一个扩展的示例。 TypeScript 提供了控制加宽过程的方法,例如使用"const"。 ### 常量 在声明变量时使用 `const` 关键字会导致 TypeScript 中的类型推断范围更窄。 For example: ```typescript const x = 'x'; // TypeScript 将 'x' 推断为带有 'const'(不可变)的字符串字面量,但将其扩展为带有 'let'(可重新赋值)的 'string'。 let y: 'y' | 'x' = 'y'; y = x; // 有效: x的类型推断为 'x' ``` 通过使用 const 声明变量 x,其类型被缩小为特定的文字值"x"。由于 x 的类型被缩小,因此可以将其赋值给变量 y 而不会出现任何错误。可以推断类型的原因是因为 const 变量无法重新分配,因此它们的类型可以缩小到特定的文字类型,在本例中为字面量类型"x"。 #### 类型参数的 const 修饰符 从 TypeScript 5.0 版本开始,可以 `const` 在泛型类型参数上指定属性。这可以推断出最精确的类型。让我们看一个不使用 `const` 的示例: ```typescript function identity(value: T) { // 这里没有const return value; } const values = identity({ a: 'a', b: 'b' }); // 类型推断为: { a: string; b: string; } ``` 正如您所看到的,属性a和b是通过 类型推断出来的string 。 现在,让我们看看 `const` 版本的差异: ```typescript function identity(value: T) { // 对类型参数使用 const 修饰符 return value; } const values = identity({ a: 'a', b: 'b' }); // 类型推断为: { a: "a"; b: "b"; } ``` 现在我们可以看到属性 `a` 和 `b` 被推断为const,因此 `a` 和 `b`被视为字符串文字而不仅仅是 `string` 类型。 ### 常量断言 此功能允许您根据变量的初始化值声明具有更精确的文字类型的变量,这向编译器表明该值应被视为不可变文字。 这里有一些例子: 在单个属性上: ```typescript const v = { x: 3 as const, }; v.x = 3; ``` 在整个对象上: ```typescript const v = { x: 1, y: 2, } as const; ``` 这在定义元组的类型时特别有用: ```typescript const x = [1, 2, 3]; // number[] const y = [1, 2, 3] as const; // 只读数组 [1, 2, 3] ``` ### 显式类型注释 我们可以具体地传递一个类型,在下面的示例中,属性x的类型是number: ```typescript const v = { x: 1, // 推断类型: number (加宽了) }; v.x = 3; // 有效 ``` 我们可以通过使用字面量类型的联合使类型注释更加具体: ```typescript const v: { x: 1 | 2 | 3 } = { x: 1, // x 现在是字面量的联合类型: 1 | 2 | 3 }; v.x = 3; // 有效 v.x = 100; // 无效的 ``` ### 类型缩小 类型缩小是 TypeScript 中的一个过程,其中一般类型缩小为更具体的类型。当 TypeScript 分析代码并确定某些条件或操作可以细化类型信息时,就会发生这种情况。 缩小类型可以通过不同的方式发生,包括: #### 条件 通过使用条件语句(比如 `if` 或 `switch`),TypeScript 可以根据条件的结果缩小类型范围。例如: ```typescript let x: number | undefined = 10; if (x !== undefined) { x += 100; // 由于条件判断,类型被缩小为 number } ``` #### 抛错或者返回 抛出错误或从分支提前返回可用于帮助 TypeScript 缩小类型范围。例如: ```typescript let x: number | undefined = 10; if (x === undefined) { throw 'error'; } x += 100; ``` 在 TypeScript 中缩小类型范围的其他方法包括: * `instanceof` 操作: 用于检查对象是否是特定类的实例。 * `in` 操作: 用于检查对象中是否存在属性。 * `typeof` 操作: 用于在运行时检查值的类型。 * 内部函数,比如: `Array.isArray()`: 用于检查值是否为数组。 #### 可区分联合 使用"可区分联合"是 TypeScript 中的一种模式,其中向对象添加显式"标签"以区分联合内的不同类型。该模式也称为"标记联合"。在以下示例中,"tag"由属性"type"表示: ```typescript type A = { type: 'type_a'; value: number }; type B = { type: 'type_b'; value: string }; const x = (input: A | B): string | number => { switch (input.type) { case 'type_a': return input.value + 100; // 类型为 A case 'type_b': return input.value + 'extra'; // 类型为 B } }; ``` #### 用户定义的类型保护 在 TypeScript 无法确定类型的情况下,可以编写一个称为"用户定义类型保护"的辅助函数。在下面的示例中,我们将在应用某些过滤后利用类型谓词来缩小类型范围: ```typescript const data = ['a', null, 'c', 'd', null, 'f']; const r1 = data.filter(x => x != null); // 类型为 (string | null)[], TypeScript 不能准确推断类型 const isValid = (item: string | null): item is string => item !== null; // 自定义类型保护 const r2 = data.filter(isValid); // 类型现在为 string[], 通过使用断言类型保护,我们能够缩小类型 ``` ## 原始类型 TypeScript 支持 7 种基本类型。原始数据类型是指不是对象并且没有任何与其关联的方法的类型。在 TypeScript 中,所有原始类型都是不可变的,这意味着它们的值一旦分配就无法更改。 ### string 原始 `string` 类型存储文本数据,并且值始终是双引号或单引号的。 ```typescript const x: string = 'x'; const y: string = 'y'; ``` 如果字符串被反引号 (`) 字符包围,则字符串可以跨越多行: ```typescript let sentence: string = `xxx, yyy`; ``` ### boolean TypeScript 中的数据 `boolean` 类型存储二进制值,或者true或false。 ```typescript const isReady: boolean = true; ``` ### number TypeScript 中的数据类型 `number` 用 64 位浮点值表示。类型 `number` 可以表示整数和分数。TypeScript 还支持十六进制、二进制和八进制,例如: ```typescript const decimal: number = 10; const hexadecimal: number = 0xa00d; // 十六进制数以 0x 开始 const binary: number = 0b1010; // 二进制数以 0b 开始 const octal: number = 0o633; // 八进制数以 0o 开始 ``` ### bigInt `bigInt` 表示无法用 `number` 表示的非常大的数值 (253 – 1)。 `bigInt` 可以通过调用内置函数 `BigInt()` 或添加 `n` 到任何整数数字字面量的末尾来创建: ```typescript const x: bigint = BigInt(9007199254740991); const y: bigint = 9007199254740991n; ``` 笔记: * `bigInt` 值不能与 `number` 混合,也不能与内置的 `Math` 一起使用,它们必须强制为相同的类型。 * 仅当目标配置为 ES2020 或更高版本时,“bigInt”值才可用。 ### symbol JavaScript 有一个原始函数 Symbol(),它创建一个全局唯一的引用。 ```typescript let sym = Symbol('x'); // symbol 类型 ``` ### null and undefined `null`和 `undefined` 类型都表示没有值或不存在任何值。 `undefined` 类型意味着该值未分配或初始化,或者指示无意中缺少值。 `null` 类型意味着我们知道该字段没有值,因此值不可用,这表明故意不存在值。 ### Array `array` 是一种可以存储多个相同类型或不同类型的值的数据类型。可以使用以下语法定义它: ```typescript const x: string[] = ['a', 'b']; const y: Array = ['a', 'b']; const j: Array = ['a', 1, 'b', 2]; ``` TypeScript 使用以下语法支持只读数组: ```typescript const x: readonly string[] = ['a', 'b']; // 只读修饰符 const y: ReadonlyArray = ['a', 'b']; const j: ReadonlyArray = ['a', 1, 'b', 2]; j.push('x'); // 有效 ``` TypeScript 支持数组和只读数组: ```typescript const x: [string, number] = ['a', 1]; const y: readonly [string, number] = ['a', 1]; ``` ### any 数据 `any` 类型字面上代表"任何"值,当 TypeScript 无法推断类型或未指定时,它是默认值。 使用 `any` 时,TypeScript编译器会跳过类型检查,因此 `any` 使用时不存在类型安全。通常,当发生错误时不要使用 `any` 静默编译器,而是专注于修复错误,因为使用 `any` 它可能会破坏契约,并且我们会失去 TypeScript 自动完成的好处。 在从 JavaScript 逐步迁移到 TypeScript 的过程中,该 `any` 类型可能很有用,因为它可以让编译器保持沉默。 对于新项目,请使用 TypeScript 配置 `noImplicitAny` ,该配置使 TypeScript 能够在any使用或推断时发出错误。 `any` 通常是错误的来源,它可以掩盖类型的实际问题。尽可能避免使用它。 ## 类型注释 在使用 `var` 、 `let` 和 `const` 声明变量时,可以选择添加类型: ```typescript const x: number = 1; ``` TypeScript 在推断类型方面做得很好,尤其是简单类型时,因此在大多数情况下这些声明是不必要的。 在函数上可以向参数添加类型注释: ```typescript function sum(a: number, b: number) { return a + b; } ``` 以下是使用匿名函数(所谓的 lambda 函数)的示例: ```typescript const sum = (a: number, b: number) => a + b; ``` 当参数存在默认值时可以避免这些注释: ```typescript const sum = (a = 10, b: number) => a + b; ``` 可以将返回类型注释添加到函数中: ```typescript const sum = (a = 10, b: number): number => a + b; ``` 这对于更复杂的函数尤其有用,因为在实现之前编写显式返回类型可以帮助更好地思考该函数。 通常考虑注释类型签名,但不注释主体局部变量,并始终将类型添加到对象字面量中。 ## 可选属性 对象可以通过在属性名称末尾添加问号 `?` 来指定可选属性: ```typescript type X = { a: number; b?: number; // 可选的 }; ``` 当属性是可选的时,可以指定默认值 ```typescript type X = { a: number; b?: number; }; const x = ({ a, b = 100 }: X) => a + b; ``` ## 只读属性 是否可以通过使用修饰符来防止对属性进行写入,`readonly` 以确保该属性不能被重写,但不提供任何完全不变性的保证: ```typescript interface Y { readonly a: number; } type X = { readonly a: number; }; type J = Readonly<{ a: number; }>; type K = { readonly [index: number]: string; }; ``` ## 索引签名 在 TypeScript 中,我们可以使用 `string` 、`number` 和 `symbol` 作为索引签名: ```typescript type K = { [name: string | number]: string; }; const k: K = { x: 'x', 1: 'b' }; console.log(k['x']); console.log(k[1]); console.log(k['1']); // 同 k[1] 的结果相同 ``` 请注意,JavaScript 会自动将 `number` 的索引转换相同值的 'string'索引, 比如 `k[1]` 和 `k["1"]` 返回相同值。 ## 扩展类型 可以扩展 `interface`(从另一种类型复制成员): ```typescript interface X { a: string; } interface Y extends X { b: string; } ``` 还可以从多种 `interface` 进行扩展: ```typescript interface A { a: string; } interface B { b: string; } interface Y extends A, B { y: string; } ``` 该 `extends` 关键字仅适用于 `interface`,因为 `type` 使用交集: ```typescript type A = { a: number; }; type B = { b: number; }; type C = A & B; ``` 可以使用 `interface` 来扩展类 `type`,但反之则不然: ```typescript type A = { a: string; }; interface B extends A { b: string; } ``` ## 字面量类型 文字类型是来自集体类型的单个元素集,它定义了一个非常精确的值,即 JavaScript 原始数据。 TypeScript 中的文字类型是数字、字符串和布尔值。 示例如下: ```typescript const a = 'a'; // 字符串字面量类型 const b = 1; // 数字字面量类型 const c = true; // 布尔字面量类型 ``` 在以下示例中,您可以看到联合类型别名。`O` 仅包含指定的值,其他字符串均无效: ```typescript type O = 'a' | 'b' | 'c'; ``` ## 字面量推断 字面量推断是 TypeScript 中的一项功能,允许根据变量或参数的值推断其类型。 在下面的示例中,我们可以看到 TypeScript 认为x文字类型是因为该值以后不能随时更改,而y被推断为字符串,因为它以后可以随时修改。 ```typescript const x = 'x'; // x 为字面量类型, 因为值不能改变 let y = 'y'; // string, 我们能改变这个值 ``` 在下面的示例中,我们可以看到 `o.x` 被推断为 `string`(而不是字面量的a),因为 TypeScript 认为该值可以在以后随时更改。 ```typescript type X = 'a' | 'b'; let o = { x: 'a', // 这是一个更宽的 string }; const fn = (x: X) => `${x}-foo`; console.log(fn(o.x)); // 'string' 类型的参数不能赋值给 'X' 类型的参数 ``` 正如你所看到的代码在传递 `o.x` 给 `fn` 作为一个狭窄类型时,抛出了一个错误。 我们能通过使用 `const` 或者 `X` 来借助类型推断解决这个问题: ```typescript let o = { x: 'a' as const, }; ``` or: ```typescript let o = { x: 'a' as X, }; ``` ## 严格空检查 `strictNullChecks` 是一个 TypeScript 编译器选项,强制执行严格的 null 检查。启用此选项后,只有在变量和参数已使用联合类型 `null` | `undefined` 显式声明为该类型时,才可以对其进行赋值`null` 或者 `undefined`。如果变量或参数未显式声明为可为空,TypeScript 将生成错误以防止潜在的运行时错误。 ## 枚举 在 TypeScript 中,枚举是一组命名常量值。 ```typescript enum Color { Red = '#ff0000', Green = '#00ff00', Blue = '#0000ff', } ``` 枚举可以用不同的方式定义: ### 数字枚举 在 TypeScript 中,数字枚举是一个枚举,其中每个常量都分配有一个数值,默认从 0 开始。 ```typescript enum Size { Small, // 值从 0 开始 Medium, Large, } ``` 可以通过显式分配来指定自定义值: ```typescript enum Size { Small = 10, Medium, Large, } console.log(Size.Medium); // 11 ``` ### 字符串枚举 在 TypeScript 中,字符串枚举是每个常量都分配有一个字符串值的枚举。 ```typescript enum Language { English = 'EN', Spanish = 'ES', } ``` 注意:TypeScript 允许使用异构枚举,其中字符串和数字成员可以共存。 ### 常量枚举 TypeScript 中的常量枚举是一种特殊类型的枚举,其中所有值在编译时都是已知的,并且在使用枚举的任何地方都会内联,从而产生更高效的代码。 ```typescript const enum Language { English = 'EN', Spanish = 'ES', } console.log(Language.English); ``` 将被编译成: ```typescript console.log('EN' /* Language.English */); ``` 注意:常量枚举具有硬编码值,擦除枚举,这在独立库中可能更有效,但通常是不可取的。此外,常量枚举不能有计算成员。 ### 反向映射 在 TypeScript 中,枚举中的反向映射是指从值中检索枚举成员名称的能力。默认情况下,枚举成员具有从名称到值的正向映射,但可以通过为每个成员显式设置值来创建反向映射。当您需要按枚举成员的值查找枚举成员,或者需要迭代所有枚举成员时,反向映射非常有用。需要注意的是,只有数字类型的枚举成员会生成反向映射,字符串类型的枚举成员则不会。 以下枚举: ```typescript enum Grade { A = 90, B = 80, C = 70, F = 'fail', } ``` 编译为: ```javascript 'use strict'; var Grade; (function (Grade) { Grade[(Grade['A'] = 90)] = 'A'; Grade[(Grade['B'] = 80)] = 'B'; Grade[(Grade['C'] = 70)] = 'C'; Grade['F'] = 'fail'; })(Grade || (Grade = {})); ``` 由此可见,对数字类型的枚举成员,可以从枚举值映射回枚举名称,但对字符串类型的枚举成员无法这样做。 ```typescript enum Grade { A = 90, B = 80, C = 70, F = 'fail', } const myGrade = Grade.A; console.log(Grade[myGrade]); // A console.log(Grade[90]); // A const failGrade = Grade.F; console.log(failGrade); // fail console.log(Grade[failGrade]); // 因为索引表达式的类型不是 'number',所以元素是隐式的 'any' 类型。 ``` ### 环境枚举 TypeScript 中的环境枚举是一种在声明文件 (*.d.ts) 中定义的枚举类型,没有关联的实现。它允许您定义一组命名常量,这些常量可以在不同文件中以类型安全的方式使用,而无需在每个文件中导入实现细节。 ### 计算成员和常量成员 在 TypeScript 中,计算成员是枚举的成员,其值在运行时计算,而常量成员是其值在编译时设置且在运行时无法更改的成员。常规枚举中允许使用计算成员,而常规枚举和常量枚举中都允许使用常量成员。 ```typescript // 常量成员 enum Color { Red = 1, Green = 5, Blue = Red + Green, } console.log(Color.Blue); // 6 编译时生成 ``` ```typescript // 计算成员 enum Color { Red = 1, Green = Math.pow(2, 2), Blue = Math.floor(Math.random() * 3) + 1, } console.log(Color.Blue); // 运行时生成的随机数 ``` 枚举由包含其成员类型的联合表示。每个成员的值可以通过常量或非常量表达式确定,拥有常量值的成员被分配字面量类型。为了说明这一点,请考虑类型 E 及其子类型 E.A、E.B 和 E.C 的声明。在本例中,E 表​​示联合 E.A | E.B | E.C 。 ```typescript const identity = (value: number) => value; enum E { A = 2 * 5, // 数字字面量 B = 'bar', // 字符串字面量 C = identity(42), // 不透明计算 } console.log(E.C); //42 ``` ## 缩小范围 TypeScript 缩小范围是细化条件块内变量类型的过程。这在使用联合类型时很有用,其中一个变量可以有多个类型。 TypeScript 可识别多种缩小类型范围的方法: ### typeof 类型保护 typeof 类型保护是 TypeScript 中的一种特定类型保护,它根据变量的内置 JavaScript 类型检查变量的类型。 ```typescript const fn = (x: number | string) => { if (typeof x === 'number') { return x + 1; // x 是数字 } return -1; }; ``` ### 真实性缩小 TypeScript 中的真实性缩小是通过检查变量是真还是假来相应地缩小其类型来实现的。 ```typescript const toUpperCase = (name: string | null) => { if (name) { return name.toUpperCase(); } else { return null; } }; ``` ### 相等缩小 TypeScript 中的相等缩小通过检查变量是否等于特定值来相应缩小其类型。 它与`switch`语句和等号运算符(例如`===`、`!==`、`==`和`!=`)结合使用来缩小类型范围。 ```typescript const checkStatus = (status: 'success' | 'error') => { switch (status) { case 'success': return true; case 'error': return null; } }; ``` ### In运算符缩小 TypeScript 中的 `in` 运算符缩小范围是一种根据变量类型中是否存在属性来缩小变量类型的方法。 ```typescript type Dog = { name: string; breed: string; }; type Cat = { name: string; likesCream: boolean; }; const getAnimalType = (pet: Dog | Cat) => { if ('breed' in pet) { return 'dog'; } else { return 'cat'; } }; ``` ### instanceof 缩小 TypeScript 中的 `instanceof` 运算符缩小是一种根据变量的构造函数缩小变量类型的方法,方法是检查对象是否是某个类或接口的实例。 ```typescript class Square { constructor(public width: number) {} } class Rectangle { constructor( public width: number, public height: number ) {} } function area(shape: Square | Rectangle) { if (shape instanceof Square) { return shape.width * shape.width; } else { return shape.width * shape.height; } } const square = new Square(5); const rectangle = new Rectangle(5, 10); console.log(area(square)); // 25 console.log(area(rectangle)); // 50 ``` ## 赋值 使用赋值缩小 TypeScript 是一种根据分配给变量的值来缩小变量类型的方法。当为变量分配值时,TypeScript 会根据分配的值推断其类型,并缩小变量的类型以匹配推断的类型。 ```typescript let value: string | number; value = 'hello'; if (typeof value === 'string') { console.log(value.toUpperCase()); } value = 42; if (typeof value === 'number') { console.log(value.toFixed(2)); } ``` ## 控制流分析 TypeScript 中的控制流分析是一种静态分析代码流以推断变量类型的方法,允许编译器根据分析结果根据需要缩小这些变量的类型。 在 TypeScript 4.4 之前,代码流分析仅适用于 if 语句中的代码,但从 TypeScript 4.4 开始,它还可以应用于条件表达式和通过 const 变量间接引用的判别式属性访问。 例如: ```typescript const f1 = (x: unknown) => { const isString = typeof x === 'string'; if (isString) { x.length; } }; const f2 = ( obj: { kind: 'foo'; foo: string } | { kind: 'bar'; bar: number } ) => { const isFoo = obj.kind === 'foo'; if (isFoo) { obj.foo; } else { obj.bar; } }; ``` 一些未发生缩小的示例: ```typescript const f1 = (x: unknown) => { let isString = typeof x === 'string'; if (isString) { x.length; // 错误, 没有缩小,因为 isString 不是常量 } }; const f6 = ( obj: { kind: 'foo'; foo: string } | { kind: 'bar'; bar: number } ) => { const isFoo = obj.kind === 'foo'; obj = obj; if (isFoo) { obj.foo; // 错误, 没有缩小,因为 obj 在函数体中被赋值 } }; ``` 注意: 在条件表达式中最多分析五个间接级别。 ## 类型谓词 TypeScript 中的类型谓词是返回布尔值的函数,用于将变量的类型缩小为更具体的类型。 ```typescript const isString = (value: unknown): value is string => typeof value === 'string'; const foo = (bar: unknown) => { if (isString(bar)) { console.log(bar.toUpperCase()); } else { console.log('not a string'); } }; ``` ## 可区分联合 TypeScript 中的可区分联合是一种联合类型,它使用称为判别式的公共属性来缩小联合的可能类型集。 ```typescript type Square = { kind: 'square'; // 判别式 size: number; }; type Circle = { kind: 'circle'; // 判别式 radius: number; }; type Shape = Square | Circle; const area = (shape: Shape) => { switch (shape.kind) { case 'square': return Math.pow(shape.size, 2); case 'circle': return Math.PI * Math.pow(shape.radius, 2); } }; const square: Square = { kind: 'square', size: 5 }; const circle: Circle = { kind: 'circle', radius: 2 }; console.log(area(square)); // 25 console.log(area(circle)); // 12.566370614359172 ``` ## never 类型 当变量缩小为不能包含任何值的类型时,TypeScript 编译器将推断该变量必须属于该never类型。这是因为 never 类型代表永远无法生成的值。 ```typescript const printValue = (val: string | number) => { if (typeof val === 'string') { console.log(val.toUpperCase()); } else if (typeof val === 'number') { console.log(val.toFixed(2)); } else { // val 在这里的类型为 never,因为它只能是字符串或数字 const neverVal: never = val; console.log(`Unexpected value: ${neverVal}`); } }; ``` ## 详尽性检查 详尽性检查是 TypeScript 中的一项功能,可确保在 `switch` 语句或 `if` 语句中处理可区分联合的所有可能情况。 ```typescript type Direction = 'up' | 'down'; const move = (direction: Direction) => { switch (direction) { case 'up': console.log('Moving up'); break; case 'down': console.log('Moving down'); break; default: const exhaustiveCheck: never = direction; console.log(exhaustiveCheck); // 这行永远不会被执行 } }; ``` 该 `never` 类型用于确保默认情况是详尽的,并且如果将新值添加到 Direction 类型而未在 switch 语句中进行处理,则 TypeScript 将引发错误。 ## 对象类型 在 TypeScript 中,对象类型描述对象的形状。它们指定对象属性的名称和类型,以及这些属性是必需的还是可选的。 在 TypeScript 中,您可以通过两种主要方式定义对象类型: 通过指定对象属性的名称、类型和可选性来定义对象的形状的接口。 ```typescript interface User { name: string; age: number; email?: string; } ``` 类型别名与接口类似,定义了对象的形状。但是,它还可以基于现有类型或现有类型的组合创建新的自定义类型。这包括定义联合类型、交集类型和其他复杂类型。 ```typescript type Point = { x: number; y: number; }; ``` 也可以匿名定义类型: ```typescript const sum = (x: { a: number; b: number }) => x.a + x.b; console.log(sum({ a: 5, b: 1 })); ``` ## 元组类型(匿名) 元组类型是一种表示具有固定数量的元素及其相应类型的数组的类型。元组类型以固定顺序强制执行特定数量的元素及其各自的类型。当您想要表示具有特定类型的值的集合时,元组类型非常有用,其中数组中每个元素的位置都有特定的含义。 ```typescript type Point = [number, number]; ``` ## 命名元组类型(已标记) 元组类型可以包含每个元素的可选标签或名称。 这些标签用于提高可读性和工具帮助,不会影响您可以使用它们执行的操作。 ```typescript type T = string; type Tuple1 = [T, T]; type Tuple2 = [a: T, b: T]; type Tuple3 = [a: T, T]; // 命名元组加匿名元组 ``` ## 固定长度元组 固定长度元组是一种特定类型的元组,它强制执行特定类型的固定数量的元素,并且一旦定义元组就不允许对其长度进行任何修改。 当您需要表示具有特定数量的元素和特定类型的值的集合,并且您希望确保元组的长度和类型不会无意中更改时,固定长度元组非常有用。 ```typescript const x = [10, 'hello'] as const; x.push(2); // 错误 ``` ## 联合类型 联合类型是一种表示值的类型,该值可以是多种类型之一。联合类型使用 `|` 表示 每种可能类型之间的符号。 ```typescript let x: string | number; x = 'hello'; // 有效 x = 123; // 有效 ``` ## 交集类型 交集类型是表示具有两种或多种类型的所有属性的值的类型。交叉类型在每种类型之间使用 & 符号表示。 ```typescript type X = { a: string; }; type Y = { b: string; }; type J = X & Y; // 交集 const j: J = { a: 'a', b: 'b', }; ``` ## 类型索引 类型索引是指能够通过预先未知的键来定义可以索引的类型,使用索引签名来指定未显式声明的属性的类型。 ```typescript type Dictionary = { [key: string]: T; }; const myDict: Dictionary = { a: 'a', b: 'b' }; console.log(myDict['a']); // 返回 a ``` ## 值的类型 TypeScript 中的"Type from Value"是指通过类型推断从值或表达式自动推断出类型。 ```typescript const x = 'x'; // TypeScript 可以自动推断变量的类型是 string ``` ## Func 返回值的类型 Func Return 中的类型是指根据函数的实现自动推断函数的返回类型的能力。这允许 TypeScript 无需显式类型注释即可确定函数返回值的类型。 ```typescript const add = (x: number, y: number) => x + y; // TypeScript 可以推断函数的返回类型是数字 ``` ## 模块的类型 模块的类型是指使用模块的导出值自动推断其类型的能力。当模块导出特定类型的值时,TypeScript 可以使用该信息在将该值导入到另一个模块时自动推断该值的类型。 ```typescript export const add = (x: number, y: number) => x + y; // index.ts import { add } from 'calc'; const r = add(1, 2); // r 是 number 类型 ``` ## 映射类型 TypeScript 中的映射类型允许您通过使用映射函数转换每个属性来基于现有类型创建新类型。通过映射现有类型,您可以创建以不同格式表示相同信息的新类型。要创建映射类型,您可以使用运算符访问现有类型的属性 `keyof` ,然后更改它们以生成新类型。在以下示例中: ```typescript type MyMappedType = { [P in keyof T]: T[P][]; }; type MyType = { foo: string; bar: number; }; type MyNewType = MyMappedType; const x: MyNewType = { foo: ['hello', 'world'], bar: [1, 2, 3], }; ``` 我们定义 MyMappedType 来映射 T 的属性,创建一个新类型,其中每个属性都是其原始类型的数组。使用它,我们创建 MyNewType 来表示与 MyType 相同的信息,但每个属性都是一个数组。 ## 映射类型修饰符 TypeScript 中的映射类型修饰符支持对现有类型中的属性进行转换: * `readonly` 或 `+readonly`:这会将映射类型中的属性呈现为只读。 * `-readonly`:这允许映射类型中的属性是可变的。 * `?`:这将映射类型中的属性指定为可选。 例子: ```typescript type ReadOnly = { readonly [P in keyof T]: T[P] }; // 所有属性标记为只读 type Mutable = { -readonly [P in keyof T]: T[P] }; // 所有标记为可变的属性 type MyPartial = { [P in keyof T]?: T[P] }; // 所有标记为可选的属性 ```` ## 条件类型 条件类型是一种创建依赖于条件的类型的方法,其中要创建的类型是根据条件的结果确定的。它们是使用 `extends` 关键字和三元运算符来定义的,以便有条件地在两种类型之间进行选择。 ```typescript type IsArray = T extends any[] ? true : false; const myArray = [1, 2, 3]; const myNumber = 42; type IsMyArrayAnArray = IsArray; // true 类型 type IsMyNumberAnArray = IsArray; // false 类型 ``` ## 分配条件类型 分布式条件类型是一种功能,通过单独对联合的每个成员应用转换,允许类型分布在类型的联合上。当使用映射类型或高阶类型时,这尤其有用。 ```typescript type Nullable = T extends any ? T | null : never; type NumberOrBool = number | boolean; type NullableNumberOrBool = Nullable; // number | boolean | null ``` ## infer 条件类型中的类型推断 `infer` 关键字在条件类型中使用,用于从依赖于泛型参数的类型中推断(提取)泛型参数的类型。这允许您编写更灵活且可重用的类型定义。 ```typescript type ElementType = T extends (infer U)[] ? U : never; type Numbers = ElementType; // number type Strings = ElementType; // string ``` ## 预定义条件类型 在 TypeScript 中,预定义的条件类型是语言提供的内置条件类型。它们旨在根据给定类型的特征执行常见的类型转换。 `Exclude`: 此类型从 Type 中删除可分配给 ExcludedType 的所有类型。 `Extract`: 此类型从 Union 中提取可分配给 Type 的所有类型。 `NonNullable`: 此类型从 Type 中删除 null 和 undefined。 `ReturnType`: 此类型提取函数 Type 的返回类型。 `Parameters`: 该类型提取函数类型的参数类型。 `Required`: 此类型使 Type 中的所有属性成为必需。 `Partial`: 此类型使 Type 中的所有属性都是可选的。 `Readonly`: 此类型使 Type 中的所有属性变为只读。 ## 模板联合类型 模板联合类型可用于合并和操作类型系统内的文本,例如: ```typescript type Status = 'active' | 'inactive'; type Products = 'p1' | 'p2'; type ProductId = `id-${Products}-${Status}`; // "id-p1-active" | "id-p1-inactive" | "id-p2-active" | "id-p2-inactive" ``` ## 任意类型 `any` 类型是一种特殊类型(通用超类型),可用于表示任何类型的值(基元、对象、数组、函数、错误、符号)。它通常用于编译时未知值类型的情况,或者使用来自没有 TypeScript 类型的外部 API 或库的值时。 使用 `any` 类型,就等于告诉 TypeScript 编译器,值应该不受任何限制地表示。为了最大限度地提高代码的类型安全性,请考虑以下几点: * 将 `any` 的使用限制在类型确实未知的特定情况下。 * 不要从函数返回 `any` 类型,因为这会削弱使用该函数的代码的类型安全性。 * 如果您需要使编译器保持沉默,请使用 `@ts-ignore` 而不是 `any`。 ```typescript let value: any; value = true; // 有效 value = 7; // 有效 ``` ## 未知类型 在 TypeScript 中,未知类型表示未知类型的值。与允许任何类型值的 `any` 类型不同,`unknown` 需要在以特定方式使用它之前进行类型检查或断言,因此在未首先断言或缩小到更具体的类型的情况下,不允许对 `unknown` 进行任何操作 。 `unknown` 类型只能分配给任何类型和未知类型本身,它是any 的类型安全替代方案。 ```typescript let value: unknown; let value1: unknown = value; // 有效 let value2: any = value; // 有效 let value3: boolean = value; // 无效 let value4: number = value; // 无效 ``` ```typescript const add = (a: unknown, b: unknown): number | undefined => typeof a === 'number' && typeof b === 'number' ? a + b : undefined; console.log(add(1, 2)); // 3 console.log(add('x', 2)); // undefined ``` ## 空类型 `void` 类型用于指示函数不返回值。 ```typescript const sayHello = (): void => { console.log('Hello!'); }; ``` ## Never类型 `never` 类型表示从未出现过的值。它用于表示从不返回或抛出错误的函数或表达式。 例如无限循环: ```typescript const infiniteLoop = (): never => { while (true) { // 做点什么 } }; ``` 抛出错误: ```typescript const throwError = (message: string): never => { throw new Error(message); }; ``` `never` 类型对于确保类型安全和捕获代码中的潜在错误很有用。当与其他类型和控制流语句结合使用时,它可以帮助 TypeScript 分析和推断更精确的类型,例如: ```typescript type Direction = 'up' | 'down'; const move = (direction: Direction): void => { switch (direction) { case 'up': // 向上移动 break; case 'down': // 向下移动 break; default: const exhaustiveCheck: never = direction; throw new Error(`Unhandled direction: ${exhaustiveCheck}`); } }; ``` ## 接口及类型 ### 通用语法 在 TypeScript 中,接口定义对象的结构,指定对象必须具有的属性或方法的名称和类型。在 TypeScript 中定义接口的常用语法如下: ```typescript interface InterfaceName { property1: Type1; // ... method1(arg1: ArgType1, arg2: ArgType2): ReturnType; // ... } ``` 类型定义也类似: ```typescript type TypeName = { property1: Type1; // ... method1(arg1: ArgType1, arg2: ArgType2): ReturnType; // ... }; ``` `interface InterfaceName` 或者 `type TypeName`: 定义接口的名称。 `property1`: `Type1`: 指定接口的属性及其相应的类型。可以定义多个属性,每个属性用分号分隔。 `method1(arg1: ArgType1, arg2: ArgType2): ReturnType;`: 指定接口的方法。方法用其名称进行定义,后跟括号中的参数列表和返回类型。可以定义多个方法,每个方法用分号分隔。 接口示例: ```typescript interface Person { name: string; age: number; greet(): void; } ``` 类型示例: ```typescript type TypeName = { property1: string; method1(arg1: string, arg2: string): string; }; ``` 在 TypeScript 中,类型用于定义数据的形状并强制执行类型检查。在 TypeScript 中定义类型有几种常见的语法,具体取决于具体的用例。这里有些例子: ### 基本类型 ```typescript let myNumber: number = 123; // 数字类型 let myBoolean: boolean = true; // 布尔类型 let myArray: string[] = ['a', 'b']; // 字符串数组 let myTuple: [string, number] = ['a', 123]; // 元组 ``` ### 对象和接口 ```typescript const x: { name: string; age: number } = { name: 'Simon', age: 7 }; ``` ### 并集和交集类型 ```typescript type MyType = string | number; // 并集 let myUnion: MyType = 'hello'; // 可以是字符串 myUnion = 123; // 或者是一个数字 type TypeA = { name: string }; type TypeB = { age: number }; type CombinedType = TypeA & TypeB; // 交集 let myCombined: CombinedType = { name: 'John', age: 25 }; // 对象同时有name和age属性 ``` ## 内置原始数据类型 TypeScript 有几个内置的原属数据类型,可用于定义变量、函数参数和返回类型: `number`: 表示数值,包括整数和浮点数。 `string`: 代表文本数据。 `boolean`: 代表逻辑值,可以是 true 或 false。 `null`: 表示没有值。 `undefined`: 表示尚未赋值或未定义的值。 `symbol`: 代表唯一标识符。符号通常用作对象属性的键。 `bigint`: 表示任意精度整数。 `any`: 代表动态或未知类型。any 类型的变量可以保存任何类型的值,并且它们绕过类型检查。 `void`: 表示不存在任何类型。它通常用作不返回值的函数的返回类型。 `never`: 表示从未出现过的值的类型。它通常用作引发错误或进入无限循环的函数的返回类型。 ## 常见的内置JS对象 TypeScript 是 JavaScript 的超集,它包含所有常用的内置 JavaScript 对象。您可以在 Mozilla 开发者网络 (MDN) 文档网站上找到这些对象的详细列表: [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects) 以下是一些常用的内置 JavaScript 对象的列表: * Function * Object * Boolean * Error * Number * BigInt * Math * Date * String * RegExp * Array * Map * Set * Promise * Intl ## 重载 TypeScript 中的函数重载允许您为单个函数名称定义多个函数签名,从而使您能够定义可以多种方式调用的函数。这是一个例子: ```typescript // 重载 function sayHi(name: string): string; function sayHi(names: string[]): string[]; // 实现 function sayHi(name: unknown): unknown { if (typeof name === 'string') { return `Hi, ${name}!`; } else if (Array.isArray(name)) { return name.map(name => `Hi, ${name}!`); } throw new Error('Invalid value'); } sayHi('xx'); // 有效 sayHi(['aa', 'bb']); // 有效 ``` 这是在 `class` 中使用函数重载的另一个示例: ```typescript class Greeter { message: string; constructor(message: string) { this.message = message; } // 重载 sayHi(name: string): string; sayHi(names: string[]): ReadonlyArray; // 实现 sayHi(name: unknown): unknown { if (typeof name === 'string') { return `${this.message}, ${name}!`; } else if (Array.isArray(name)) { return name.map(name => `${this.message}, ${name}!`); } throw new Error('value is invalid'); } } console.log(new Greeter('Hello').sayHi('Simon')); ``` ## 合并与扩展 合并和扩展是指与使用类型和接口相关的两个不同概念。 合并允许您将多个同名声明合并到一个定义中,例如,当您多次定义同名接口时: ```typescript interface X { a: string; } interface X { b: number; } const person: X = { a: 'a', b: 7, }; ``` 扩展是指扩展或继承现有类型或接口以创建新类型或接口的能力。它是一种向现有类型添加附加属性或方法而不修改其原始定义的机制。例子: ```typescript interface Animal { name: string; eat(): void; } interface Bird extends Animal { sing(): void; } const dog: Bird = { name: 'Bird 1', eat() { console.log('Eating'); }, sing() { console.log('Singing'); }, }; ``` ## 类型和接口之间的差异 声明合并(增强): 接口支持声明合并,这意味着您可以定义多个具有相同名称的接口,TypeScript 会将它们合并为具有组合属性和方法的单个接口。 另一方面,类型不支持声明合并。 当您想要添加额外的功能或自定义现有类型而不修改原始定义或修补丢失或不正确的类型时,这可能会很有帮助。 ```typescript interface A { x: string; } interface A { y: string; } const j: A = { x: 'xx', y: 'yy', }; ``` 扩展其他类型/接口: 类型和接口都可以扩展其他类型/接口,但语法不同。 对于接口,您可以使用“extends”关键字从其他接口继承属性和方法。 但是,接口无法扩展像联合类型这样的复杂类型。 ```typescript interface A { x: string; y: number; } interface B extends A { z: string; } const car: B = { x: 'x', y: 123, z: 'z', }; ``` 对于类型,您可以使用 & 运算符将多个类型合并为单个类型(交集)。 ```typescript interface A { x: string; y: number; } type B = A & { j: string; }; const c: B = { x: 'x', y: 123, j: 'j', }; ``` 并集和交集类型: 在定义并集和交集类型时,类型更加灵活。 通过“type”关键字,您可以使用“|”运算符轻松创建联合类型,并使用“&”运算符创建交集类型。 虽然接口也可以间接表示联合类型,但它们没有对交集类型的内置支持。 ```typescript type Department = 'dep-x' | 'dep-y'; // 并集 type Person = { name: string; age: number; }; type Employee = { id: number; department: Department; }; type EmployeeInfo = Person & Employee; // 交集 ``` 接口示例: ```typescript interface A { x: 'x'; } interface B { y: 'y'; } type C = A | B; // 接口的并集 ``` ## Class ### 通用语法 TypeScript 中使用关键字 `class` 来定义类。下面,您可以看到一个示例: ```typescript class Person { private name: string; private age: number; constructor(name: string, age: number) { this.name = name; this.age = age; } public sayHi(): void { console.log( `Hello, my name is ${this.name} and I am ${this.age} years old.` ); } } ``` `class` 关键字用于定义名为 `Person` 的类。 该类有两个私有属性:类型名称 `string` 和类型年龄 `number`。 构造函数是使用 `constructor` 关键字定义的。它将姓名和年龄作为参数并将它们分配给相应的属性。 该类有一个 `public` 名为 `sayHi` 的方法,用于记录问候消息。 要在 TypeScript 中创建类的实例,可以使用 `new` 关键字,后跟类名,然后使用括号 `()`。例如: ```typescript const myObject = new Person('John Doe', 25); myObject.sayHi(); // 输出:Hello, my name is John Doe and I am 25 years old. ``` ### 构造函数 构造函数是类中的特殊方法,用于在创建类的实例时初始化对象的属性。 ```typescript class Person { public name: string; public age: number; constructor(name: string, age: number) { this.name = name; this.age = age; } sayHello() { console.log( `Hello, my name is ${this.name} and I'm ${this.age} years old.` ); } } const john = new Person('Simon', 17); john.sayHello(); ``` 可以使用以下语法重载构造函数: ```typescript type Sex = 'm' | 'f'; class Person { name: string; age: number; sex: Sex; constructor(name: string, age: number, sex?: Sex); constructor(name: string, age: number, sex: Sex) { this.name = name; this.age = age; this.sex = sex ?? 'm'; } } const p1 = new Person('Simon', 17); const p2 = new Person('Alice', 22, 'f'); ``` 在 TypeScript 中,可以定义多个构造函数重载,但只能有一个必须与所有重载兼容的实现,这可以通过使用可选参数来实现。 ```typescript class Person { name: string; age: number; constructor(); constructor(name: string); constructor(name: string, age: number); constructor(name?: string, age?: number) { this.name = name ?? 'Unknown'; this.age = age ?? 0; } displayInfo() { console.log(`Name: ${this.name}, Age: ${this.age}`); } } const person1 = new Person(); person1.displayInfo(); // Name: Unknown, Age: 0 const person2 = new Person('John'); person2.displayInfo(); // Name: John, Age: 0 const person3 = new Person('Jane', 25); person3.displayInfo(); // Name: Jane, Age: 25 ``` ### 私有和受保护的构造函数 在 TypeScript 中,构造函数可以标记为私有或受保护,这限制了它们的可访问性和使用。 私有构造函数:只能在类本身内调用。私有构造函数通常用于以下场景:您想要强制执行单例模式或将实例的创建限制为类中的工厂方法 受保护的构造函数:当您想要创建一个不应直接实例化但可以由子类扩展的基类时,受保护的构造函数非常有用。 ```typescript class BaseClass { protected constructor() {} } class DerivedClass extends BaseClass { private value: number; constructor(value: number) { super(); this.value = value; } } // 尝试直接实例化基类将导致错误 // const baseObj = new BaseClass(); // 错误:类"BaseClass"的构造函数受到保护。 // 创建派生类的实例 const derivedObj = new DerivedClass(10); ``` ### 访问修饰符 访问修饰符 `private` 、`protected` 和 `public` 用于控制 TypeScript 类中类成员(例如属性和方法)的可见性和可访问性。这些修饰符对于强制封装以及建立访问和修改类内部状态的边界至关重要。 修饰符 `private` 仅限制对包含类中的类成员的访问。 修饰符 `protected` 允许访问包含类及其派生类中的类成员。 修饰符 `public` 提供对类成员的不受限制的访问,允许从任何地方访问它。 ### Get 与 Set Getter 和 Setter 是特殊方法,允许您定义类属性的自定义访问和修改行为。它们使您能够封装对象的内部状态,并在获取或设置属性值时提供附加逻辑。在 TypeScript 中,getter 和 setter 分别使用 `get` 和 `set` 关键字定义。这是一个例子: ```typescript class MyClass { private _myProperty: string; constructor(value: string) { this._myProperty = value; } get myProperty(): string { return this._myProperty; } set myProperty(value: string) { this._myProperty = value; } } ``` ### 类中的自动访问器 TypeScript 版本 4.9 添加了对自动访问器的支持,这是即将推出的 ECMAScript 功能。它们类似于类属性,但使用"accessor"关键字声明。 ```typescript class Animal { accessor name: string; constructor(name: string) { this.name = name; } } ``` 自动访问器被"脱糖"为私有get访问set器,在无法访问的属性上运行。 ```typescript class Animal { #__name: string; get name() { return this.#__name; } set name(value: string) { this.#__name = name; } constructor(name: string) { this.name = name; } } ``` ### this 在 TypeScript 中,`this` 关键字指的是类的方法或构造函数中的当前实例。它允许您在类自己的范围内访问和修改类的属性和方法。它提供了一种在对象自己的方法中访问和操作对象内部状态的方法。 ```typescript class Person { private name: string; constructor(name: string) { this.name = name; } public introduce(): void { console.log(`Hello, my name is ${this.name}.`); } } const person1 = new Person('Alice'); person1.introduce(); // Hello, my name is Alice. ``` ### 参数属性 参数属性允许您直接在构造函数参数中声明和初始化类属性,从而避免样板代码,例如: ```typescript class Person { constructor( private name: string, public age: number ) { // 构造函数中的"private"和"public"关键字自动声明并初始化相应的类属性。 } public introduce(): void { console.log( `Hello, my name is ${this.name} and I am ${this.age} years old.` ); } } const person = new Person('Alice', 25); person.introduce(); ``` ### 抽象类 抽象类在 TypeScript 中主要用于继承,它们提供了一种定义可由子类继承的公共属性和方法的方法。当您想要定义常见行为并强制子类实现某些方法时,这非常有用。它们提供了一种创建类层次结构的方法,其中抽象基类为子类提供共享接口和通用功能。 ```typescript abstract class Animal { protected name: string; constructor(name: string) { this.name = name; } abstract makeSound(): void; } class Cat extends Animal { makeSound(): void { console.log(`${this.name} meows.`); } } const cat = new Cat('Whiskers'); cat.makeSound(); // 输出:Whiskers meows. ``` ### 使用泛型 具有泛型的类允许您定义可以与不同类型一起使用的可重用类。 ```typescript class Container { private item: T; constructor(item: T) { this.item = item; } getItem(): T { return this.item; } setItem(item: T): void { this.item = item; } } const container1 = new Container(42); console.log(container1.getItem()); // 42 const container2 = new Container('Hello'); container2.setItem('World'); console.log(container2.getItem()); // World ``` ### 装饰器 装饰器提供了一种添加元数据、修改行为、验证或扩展目标元素功能的机制。它们是在运行时执行的函数。多个装饰器可以应用于一个声明。 装饰器是实验性功能,以下示例仅与使用 ES6 的 TypeScript 版本 5 或更高版本兼容。 对于 5 之前的 TypeScript 版本,应在您的 `tsconfig.json` 中使用使`experimentalDecorators` 或在命令行中使用 `--experimentalDecorators` 来启用它们(但以下示例不起作用)。 装饰器的一些常见用例包括: * 观察属性变化。 * 观察方法调用。 * 添加额外的属性或方法。 * 运行时验证。 * 自动序列化和反序列化。 * 日志记录。 * 授权和认证。 * 错误防护。 注意:版本 5 的装饰器不允许装饰参数。 装饰器的类型: #### 类装饰器 类装饰器对于扩展现有类非常有用,例如添加属性或方法,或者收集类的实例。在下面的示例中,我们添加一个 `toString` 将类转换为字符串表示形式的方法。 ```typescript type Constructor = new (...args: any[]) => T; function toString( Value: Class, context: ClassDecoratorContext ) { return class extends Value { constructor(...args: any[]) { super(...args); console.log(JSON.stringify(this)); console.log(JSON.stringify(context)); } }; } @toString class Person { name: string; constructor(name: string) { this.name = name; } greet() { return 'Hello, ' + this.name; } } const person = new Person('Simon'); /* Logs: {"name":"Simon"} {"kind":"class","name":"Person"} */ ``` #### 属性装饰器 属性装饰器对于修改属性的行为非常有用,例如更改初始化值。在下面的代码中,我们有一个脚本将属性设置为始终大写: ```typescript function upperCase( target: undefined, context: ClassFieldDecoratorContext ) { return function (this: T, value: string) { return value.toUpperCase(); }; } class MyClass { @upperCase prop1 = 'hello!'; } console.log(new MyClass().prop1); // 日志:HELLO! ``` #### 方法装饰器 方法装饰器允许您更改或增强方法的行为。下面是一个简单记录器的示例: ```typescript function log( target: (this: This, ...args: Args) => Return, context: ClassMethodDecoratorContext< This, (this: This, ...args: Args) => Return > ) { const methodName = String(context.name); function replacementMethod(this: This, ...args: Args): Return { console.log(`LOG: Entering method '${methodName}'.`); const result = target.call(this, ...args); console.log(`LOG: Exiting method '${methodName}'.`); return result; } return replacementMethod; } class MyClass { @log sayHello() { console.log('Hello!'); } } new MyClass().sayHello(); ``` 它记录: ```shell LOG: Entering method 'sayHello'. Hello! LOG: Exiting method 'sayHello'. ``` #### Getter 和 Setter 装饰器 getter 和 setter 装饰器允许您更改或增强类访问器的行为。例如,它们对于验证属性分配很有用。这是 getter 装饰器的一个简单示例: ```typescript function range(min: number, max: number) { return function ( target: (this: This) => Return, context: ClassGetterDecoratorContext ) { return function (this: This): Return { const value = target.call(this); if (value < min || value > max) { throw 'Invalid'; } Object.defineProperty(this, context.name, { value, enumerable: true, }); return value; }; }; } class MyClass { private _value = 0; constructor(value: number) { this._value = value; } @range(1, 100) get getValue(): number { return this._value; } } const obj = new MyClass(10); console.log(obj.getValue); // 有效: 10 const obj2 = new MyClass(999); console.log(obj2.getValue); // 抛出异常: Invalid! ``` ### 装饰器元数据 装饰器元数据简化了装饰器在任何类中应用和利用元数据的过程。 他们可以访问上下文对象上的新元数据属性,该属性可以充当基元和对象的密钥。 可以通过"Symbol.metadata"在类上访问元数据信息。 元数据可用于各种目的,例如调试、序列化或使用装饰器的依赖项注入。 ```typescript //@ts-ignore Symbol.metadata ??= Symbol('Symbol.metadata'); // 简单的兼容性填充 type Context = | ClassFieldDecoratorContext | ClassAccessorDecoratorContext | ClassMethodDecoratorContext; // 上下文对象包含属性元数据: 装饰器元数据 function setMetadata(_target: any, context: Context) { // 使用基本类型值设置元数据对象 context.metadata[context.name] = true; } class MyClass { @setMetadata a = 123; @setMetadata accessor b = 'b'; @setMetadata fn() {} } const metadata = MyClass[Symbol.metadata]; // 获取元数据对象信息 console.log(JSON.stringify(metadata)); // {"bar":true,"baz":true,"foo":true} ``` ### 继承 继承是指一个类可以从另一个类(称为基类或超类)继承属性和方法的机制。派生类也称为子类或子类,可以通过添加新的属性和方法或重写现有的属性和方法来扩展和专门化基类的功能。 ```typescript class Animal { name: string; constructor(name: string) { this.name = name; } speak(): void { console.log('The animal makes a sound'); } } class Dog extends Animal { breed: string; constructor(name: string, breed: string) { super(name); this.breed = breed; } speak(): void { console.log('Woof! Woof!'); } } // 创建基类的一个实例 const animal = new Animal('Generic Animal'); animal.speak(); // The animal makes a sound // 创建派生类的一个实例 const dog = new Dog('Max', 'Labrador'); dog.speak(); // Woof! Woof!" ``` TypeScript 不支持传统意义上的多重继承,而是允许从单个基类继承。TypeScript 支持多种接口。接口可以定义对象结构的契约,类可以实现多个接口。这允许类从多个源继承行为和结构。 ```typescript interface Flyable { fly(): void; } interface Swimmable { swim(): void; } class FlyingFish implements Flyable, Swimmable { fly() { console.log('Flying...'); } swim() { console.log('Swimming...'); } } const flyingFish = new FlyingFish(); flyingFish.fly(); flyingFish.swim(); ``` TypeScript 中的关键字 `class` 与 JavaScript 类似,通常被称为语法糖。它是在 ECMAScript 2015 (ES6) 中引入的,旨在提供更熟悉的语法,以基于类的方式创建和使用对象。然而,值得注意的是,TypeScript 作为 JavaScript 的超集,最终会编译为 JavaScript,而 JavaScript 的核心仍然是基于原型的。 ### 静态成员 TypeScript 有静态成员。要访问类的静态成员,可以使用类名后跟一个点,而不需要创建对象。 ```typescript class OfficeWorker { static memberCount: number = 0; constructor(private name: string) { OfficeWorker.memberCount++; } } const w1 = new OfficeWorker('James'); const w2 = new OfficeWorker('Simon'); const total = OfficeWorker.memberCount; console.log(total); // 2 ``` ### 属性初始化 在 TypeScript 中初始化类的属性有多种方法: 内嵌: 在下面的示例中,创建类的实例时将使用这些初始值。 ```typescript class MyClass { property1: string = 'default value'; property2: number = 42; } ``` 在构造函数中: ```typescript class MyClass { property1: string; property2: number; constructor() { this.property1 = 'default value'; this.property2 = 42; } } ``` 使用构造函数参数: ```typescript class MyClass { constructor( private property1: string = 'default value', public property2: number = 42 ) { // 无需显式地将值分配给属性。 } log() { console.log(this.property2); } } const x = new MyClass(); x.log(); ``` ### 方法重载 方法重载允许一个类有多个名称相同但参数类型不同或参数数量不同的方法。这允许我们根据传递的参数以不同的方式调用方法。 ```typescript class MyClass { add(a: number, b: number): number; // 重载签名 1 add(a: string, b: string): string; // 重载签名 2 add(a: number | string, b: number | string): number | string { if (typeof a === 'number' && typeof b === 'number') { return a + b; } if (typeof a === 'string' && typeof b === 'string') { return a.concat(b); } throw new Error('Invalid arguments'); } } const r = new MyClass(); console.log(r.add(10, 5)); // 日志:15 ``` ## 泛型 泛型允许您创建可与多种类型一起使用的可重用组件和函数。使用泛型,您可以参数化类型、函数和接口,从而允许它们对不同类型进行操作,而无需事先显式指定它们。 泛型允许您使代码更加灵活和可重用。 ### 泛型类型 要定义泛型类型,可以使用尖括号 (`<>`) 来指定类型参数,例如: ```typescript function identity(arg: T): T { return arg; } const a = identity('x'); const b = identity(123); const getLen = (data: ReadonlyArray) => data.length; const len = getLen([1, 2, 3]); ``` ### 泛型类 泛型也可以应用于类,这样它们就可以通过使用类型参数来处理多种类型。这对于创建可重用的类定义非常有用,这些定义可以在保持类型安全的同时对不同的数据类型进行操作。 ```typescript class Container { private item: T; constructor(item: T) { this.item = item; } getItem(): T { return this.item; } } const numberContainer = new Container(123); console.log(numberContainer.getItem()); // 123 const stringContainer = new Container('hello'); console.log(stringContainer.getItem()); // hello ``` ### 泛型约束 可以使用关键字 `extends` 后跟类型参数必须满足的类型或接口来约束泛型参数。 在下面的示例中,T 必须正确包含 `length` 才能有效: ```typescript const printLen = (value: T): void => { console.log(value.length); }; printLen('Hello'); // 5 printLen([1, 2, 3]); // 3 printLen({ length: 10 }); // 10 printLen(123); // 无效 ``` 3.4 RC 版中引入的泛型的一个有趣功能是高阶函数类型推断,它引入了传播泛型类型参数: ```typescript declare function pipe( ab: (...args: A) => B, bc: (b: B) => C ): (...args: A) => C; declare function list(a: T): T[]; declare function box(x: V): { value: V }; const listBox = pipe(list, box); // (a: T) => { value: T[] } const boxList = pipe(box, list); // (x: V) => { value: V }[] ``` 此功能允许更轻松地键入安全的无点风格编程,这在函数式编程中很常见。 ### 泛型上下文缩小 泛型上下文缩小是 TypeScript 中的机制,允许编译器根据使用泛型参数的上下文来缩小泛型参数的类型,在条件语句中使用泛型类型时非常有用: ```typescript function process(value: T): void { if (typeof value === 'string') { // Value 的类型被缩小到 'string' 类型 console.log(value.length); } else if (typeof value === 'number') { // Value 的类型被缩小到 'number' 类型 console.log(value.toFixed(2)); } } process('hello'); // 5 process(3.14159); // 3.14 ``` ## 擦除的结构类型 在 TypeScript 中,对象不必匹配特定的、精确的类型。例如,如果我们创建一个满足接口要求的对象,我们就可以在需要该接口的地方使用该对象,即使它们之间没有显式连接。例子: ```typescript type NameProp1 = { prop1: string; }; function log(x: NameProp1) { console.log(x.prop1); } const obj = { prop2: 123, prop1: 'Origin', }; log(obj); // 有效 ``` ## 命名空间 在 TypeScript 中,命名空间用于将代码组织到逻辑容器中,防止命名冲突并提供一种将相关代码分组在一起的方法。使用关键字 `export` 允许在"外部"模块中访问名称空间。 ```typescript export namespace MyNamespace { export interface MyInterface1 { prop1: boolean; } export interface MyInterface2 { prop2: string; } } const a: MyNamespace.MyInterface1 = { prop1: true, }; ``` ## Symbols 符号是一种原始数据类型,表示不可变值,保证在程序的整个生命周期中全局唯一。 符号可以用作对象属性的键,并提供一种创建不可枚举属性的方法。 ```typescript const key1: symbol = Symbol('key1'); const key2: symbol = Symbol('key2'); const obj = { [key1]: 'value 1', [key2]: 'value 2', }; console.log(obj[key1]); // value 1 console.log(obj[key2]); // value 2 ``` 在 WeakMap 和 WeakSet 中,现在允许符号作为键。 ## 三斜杠指令 三斜杠指令是特殊注释,为编译器提供有关如何处理文件的说明。这些指令以三个连续斜杠 (`///`) 开头,通常放置在 TypeScript 文件的顶部,对运行时行为没有影响。 三斜杠指令用于引用外部依赖项、指定模块加载行为、启用/禁用某些编译器功能等等。几个例子: 引用声明文件: ```typescript /// ``` 指明模块格式: ```typescript /// ``` 启用编译器选项,在以下示例中严格模式: ```typescript /// ``` ## 类型操作 ### 从类型创建类型 是否可以通过组合、操作或转换现有类型来创建新类型。 交集类型 ( &): 允许您将多种类型组合成单一类型: ```typescript type A = { foo: number }; type B = { bar: string }; type C = A & B; // A和B的交集 const obj: C = { foo: 42, bar: 'hello' }; ``` 联合类型 (`|`): 允许您定义可以是以下几种类型之一的类型 ```typescript type Result = string | number; const value1: Result = 'hello'; const value2: Result = 42; ``` 映射类型: 允许您转换现有类型的属性以创建新类型: ```typescript type Mutable = { readonly [P in keyof T]: T[P]; }; type Person = { name: string; age: number; }; type ImmutablePerson = Mutable; // 属性变为只读 ``` 条件类型: 允许您根据某些条件创建类型: ```typescript type ExtractParam = T extends (param: infer P) => any ? P : never; type MyFunction = (name: string) => number; type ParamType = ExtractParam; // string ``` ### 索引访问类型 在 TypeScript 中,可以使用索引访问和操作另一个类型中的属性类型 `Type[Key]`。 ```typescript type Person = { name: string; age: number; }; type AgeType = Person['age']; // number ``` ```typescript type MyTuple = [string, number, boolean]; type MyType = MyTuple[2]; // boolean ``` ### 工具类型 可以使用几种内置工具来操作类型,下面列出了最常用的: #### Awaited\ 构造一个递归解包 Promise 类型的类型。 ```typescript type A = Awaited>; // string ``` #### Partial\ 构造一个类型,并将 T 的所有属性设置为可选。 ```typescript type Person = { name: string; age: number; }; type A = Partial; // { name?: string | undefined; age?: number | undefined; } ``` #### Required\ 构造一个类型,并将 T 的所有属性设置为必需。 ```typescript type Person = { name?: string; age?: number; }; type A = Required; // { name: string; age: number; } ``` #### Readonly\ 构造一个类型,并将 T 的所有属性设置为只读。 ```typescript type Person = { name: string; age: number; }; type A = Readonly; const a: A = { name: 'Simon', age: 17 }; a.name = 'John'; // 无效 ``` #### Record\ 构造一个具有类型 T 的一组属性 K 的类型。 ```typescript type Product = { name: string; price: number; }; const products: Record = { apple: { name: 'Apple', price: 0.5 }, banana: { name: 'Banana', price: 0.25 }, }; console.log(products.apple); // { name: 'Apple', price: 0.5 } ``` #### Pick\ 通过从 T 中选取指定属性 K 来构造类型。 ```typescript type Product = { name: string; price: number; }; type Price = Pick; // { price: number; } ``` #### Omit\ 通过从 T 中省略指定属性 K 来构造类型。 ```typescript type Product = { name: string; price: number; }; type Name = Omit; // { name: string; } ``` #### Exclude\ 通过从 T 中排除类型 U 的所有值来构造类型。 ```typescript type Union = 'a' | 'b' | 'c'; type MyType = Exclude; // b ``` #### Extract\ 通过从 T 中提取类型 U 的所有值来构造类型。 ```typescript type Union = 'a' | 'b' | 'c'; type MyType = Extract; // a | c ``` #### NonNullable\ 通过从 T 中排除 null 和 undefined 来构造类型。 ```typescript type Union = 'a' | null | undefined | 'b'; type MyType = NonNullable; // 'a' | 'b' ``` #### Parameters\ 提取函数类型 T 的参数类型。 ```typescript type Func = (a: string, b: number) => void; type MyType = Parameters; // [a: string, b: number] ``` #### ConstructorParameters\ 提取构造函数类型 T 的参数类型。 ```typescript class Person { constructor( public name: string, public age: number ) {} } type PersonConstructorParams = ConstructorParameters; // [name: string, age: number] const params: PersonConstructorParams = ['John', 30]; const person = new Person(...params); console.log(person); // Person { name: 'John', age: 30 } ``` #### ReturnType\ 提取函数类型 T 的返回类型。 ```typescript type Func = (name: string) => number; type MyType = ReturnType; // number ``` #### InstanceType\ 提取类类型 T 的实例类型。 ```typescript class Person { name: string; constructor(name: string) { this.name = name; } sayHello() { console.log(`Hello, my name is ${this.name}!`); } } type PersonInstance = InstanceType; const person: PersonInstance = new Person('John'); person.sayHello(); // Hello, my name is John! ``` #### ThisParameterType\ 从函数类型 T 中提取"this"参数的类型。 ```typescript interface Person { name: string; greet(this: Person): void; } type PersonThisType = ThisParameterType; // Person ``` #### OmitThisParameter\ 从函数类型 T 中删除"this"参数。 ```typescript function capitalize(this: String) { return this[0].toUpperCase + this.substring(1).toLowerCase(); } type CapitalizeType = OmitThisParameter; // () => string ``` #### ThisType\ 作为上下文类型 `this` 的一部分。 ```typescript type Logger = { log: (error: string) => void; }; let helperFunctions: { [name: string]: Function } & ThisType = { hello: function () { this.log('some error'); // 有效,因为"log"是"this"的一部分 this.update(); // 无效 }, }; ``` #### Uppercase\ 将输入类型 T 的名称设为大写。 ```typescript type MyType = Uppercase<'abc'>; // "ABC" ``` #### Lowercase\ 将输入类型 T 的名称设为小写。 ```typescript type MyType = Lowercase<'ABC'>; // "abc" ``` #### Capitalize\ 输入类型 T 的名称大写。 ```typescript type MyType = Capitalize<'abc'>; // "Abc" ``` #### Uncapitalize\ 将输入类型 T 的名称取消大写。 ```typescript type MyType = Uncapitalize<'Abc'>; // "abc" ``` #### NoInfer\ NoInfer 是一种实用类型,旨在阻止泛型函数范围内类型的自动推断。 示例: ```typescript // 泛型函数范围内类型的自动推断。 function fn(x: T[], y: T) { return x.concat(y); } const r = fn(['a', 'b'], 'c'); // 此处的类型为 ("a" | "b" | "c")[] ``` 使用 NoInfer: ```typescript // 使用 NoInfer 阻止类型推断的示例函数 function fn2(x: T[], y: NoInfer) { return x.concat(y); } const r2 = fn2(['a', 'b'], 'c'); // 错误:类型为“c”的类型参数不能分配给类型为“a”|“b”的参数。 ``` ## 其他 ### 错误和异常处理 TypeScript 允许您使用标准 JavaScript 错误处理机制捕获和处理错误: Try-Catch-Finally 块: ```typescript try { // 可能会抛出异常的代码 } catch (error) { // 处理错误 } finally { // 总是会执行的代码, finally 是可选的 } ``` 您还可以处理不同类型的错误: ```typescript try { // 可能会抛出不同类型错误的代码 } catch (error) { if (error instanceof TypeError) { // 处理 TypeError } else if (error instanceof RangeError) { // 处理 RangeError } else { // 处理其他的错误 } } ``` 自定义错误类型: 可以通过扩展 Error 来指定更具体的错误 `class` : ```typescript class CustomError extends Error { constructor(message: string) { super(message); this.name = 'CustomError'; } } throw new CustomError('This is a custom error.'); ``` ### 混合类 Mixin 类允许您将多个类的行为组合并组合成一个类。它们提供了一种重用和扩展功能的方法,而不需要深层继承链。 ```typescript abstract class Identifiable { name: string = ''; logId() { console.log('id:', this.name); } } abstract class Selectable { selected: boolean = false; select() { this.selected = true; console.log('Select'); } deselect() { this.selected = false; console.log('Deselect'); } } class MyClass { constructor() {} } // 扩展 MyClass 以包含可识别和可选择的行为 interface MyClass extends Identifiable, Selectable {} // 将 mixins 应用于类的函数 function applyMixins(source: any, baseCtors: any[]) { baseCtors.forEach(baseCtor => { Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => { let descriptor = Object.getOwnPropertyDescriptor( baseCtor.prototype, name ); if (descriptor) { Object.defineProperty(source.prototype, name, descriptor); } }); }); } // 将 mixins 应用到 MyClass applyMixins(MyClass, [Identifiable, Selectable]); let o = new MyClass(); o.name = 'abc'; o.logId(); o.select(); ``` ### 异步语言特性 由于 TypeScript 是 JavaScript 的超集,因此它内置了 JavaScript 的异步语言功能,例如: Promises: Promise 是一种处理异步操作及其结果的方法,使用 `.then()`和等方法 `.catch()` 来处理成功和错误条件。 要了解更多信息: [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) Async/await: Async/await 关键字是一种为处理 Promise 提供看起来更同步的语法的方法。`async` 关键字用于定义异步函数,并且 `await` 关键字在异步函数中使用以暂停执行,直到 Promise 被解决或拒绝。 要了解更多信息: [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function) [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await) TypeScript 很好地支持以下 API: Fetch API: [https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) Web Workers: [https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API) Shared Workers: [https://developer.mozilla.org/en-US/docs/Web/API/SharedWorker](https://developer.mozilla.org/en-US/docs/Web/API/SharedWorker) WebSocket: [https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API) ### 迭代器和生成器 TypeScript 对迭代器和生成器都提供了很好的支持。 迭代器是实现迭代器协议的对象,提供了一种逐个访问集合或序列元素的方法。它是一个包含指向迭代中下一个元素的指针的结构。他们有一个 `next()` 方法返回序列中的下一个值以及指示序列是否为 的布尔值 `done` 。 ```typescript class NumberIterator implements Iterable { private current: number; constructor( private start: number, private end: number ) { this.current = start; } public next(): IteratorResult { if (this.current <= this.end) { const value = this.current; this.current++; return { value, done: false }; } else { return { value: undefined, done: true }; } } [Symbol.iterator](): Iterator { return this; } } const iterator = new NumberIterator(1, 3); for (const num of iterator) { console.log(num); } ``` 生成器是使用 `function*` 简化迭代器创建的语法定义的特殊函数。它们使用 `yield` 关键字来定义值的序列,并在请求值时自动暂停和恢复执行。 生成器使创建迭代器变得更加容易,并且对于处理大型或无限序列特别有用。 例子: ```typescript function* numberGenerator(start: number, end: number): Generator { for (let i = start; i <= end; i++) { yield i; } } const generator = numberGenerator(1, 5); for (const num of generator) { console.log(num); } ``` TypeScript 还支持异步迭代器和异步生成器。 要了解更多信息: [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Generator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Generator) [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Iterator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Iterator) ### TsDocs JSDoc 参考 使用 JavaScript 代码库时,可以通过使用 JSDoc 注释和附加注释来提供类型信息,帮助 TypeScript 推断正确的类型。 例子: ```typescript /** * Computes the power of a given number * @constructor * @param {number} base – The base value of the expression * @param {number} exponent – The exponent value of the expression */ function power(base: number, exponent: number) { return Math.pow(base, exponent); } power(10, 2); // function power(base: number, exponent: number): number ``` 此链接提供了完整文档: [https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html](https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html) 从版本 3.7 开始,可以从 JavaScript JSDoc 语法生成 .d.ts 类型定义。更多信息可以在这里找到: [https://www.typescriptlang.org/docs/handbook/declaration-files/dts-from-js.html](https://www.typescriptlang.org/docs/handbook/declaration-files/dts-from-js.html) ### @types @types 组织下的包是特殊的包命名约定,用于为现有 JavaScript 库或模块提供类型定义。例如使用: ```shell npm install --save-dev @types/lodash ``` 将在您当前的项目中安装 `lodash` 的类型定义。 要为 @types 包的类型定义做出贡献,请向 [https://github.com/DefinitelyTyped/DefinitelyTyped](https://github.com/DefinitelyTyped/DefinitelyTyped) 提交pr请求。 ### JSX JSX (JavaScript XML) 是 JavaScript 语言语法的扩展,允许您在 JavaScript 或 TypeScript 文件中编写类似 HTML 的代码。它通常在 React 中用来定义 HTML 结构。 TypeScript 通过提供类型检查和静态分析来扩展 JSX 的功能。 要使用 JSX,您需要在文件 `tsconfig.json` 中设置 `jsx` 编译器选项。两个常见的配置选项: * "preserve": 触发 .jsx 文件且 JSX 不变. 此选项告诉 TypeScript 按原样保留 JSX 语法,而不是在编译过程中对其进行转换。 如果您有单独的工具(例如 Babel)来处理转换,则可以使用此选项。 * "react": 启用 TypeScript 的内置 JSX 转换。 将使用 React.createElement 。 所有选项均可在此处使用: [https://www.typescriptlang.org/tsconfig#jsx](https://www.typescriptlang.org/tsconfig#jsx) ### ES6 模块 TypeScript 确实支持 ES6 (ECMAScript 2015) 和许多后续版本。这意味着您可以使用 ES6 语法,例如箭头函数、模板文字、类、模块、解构等等。 要在项目中启用 ES6 功能,您可以在 `tsconfig.json` 中指定 `target` 属性。 配置示例: ```json { "compilerOptions": { "target": "es6", "module": "es6", "moduleResolution": "node", "sourceMap": true, "outDir": "dist" }, "include": ["src"] } ``` ### ES7 求幂运算符 求幂 (`**`) 运算符计算通过将第一个操作数进行第二个操作数的幂获得的值。它的功能与 `Math.pow()` 类似,但增加了接受 BigInts 作为操作数的功能。TypeScript 完全支持在 `tsconfig.json` 文件中设置 `target` 为 `es2016`或更大版本来使用此运算符。 ```typescript console.log(2 ** (2 ** 2)); // 16 ``` ### for-await-of 语句 这是 TypeScript 完全支持的 JavaScript 功能,它允许您从目标版本 `es2018` 迭代异步可迭代对象。 ```typescript async function* asyncNumbers(): AsyncIterableIterator { yield Promise.resolve(1); yield Promise.resolve(2); yield Promise.resolve(3); } (async () => { for await (const num of asyncNumbers()) { console.log(num); } })(); ``` ### New target 元属性 您可以在 TypeScript 中使用 `new.target` 元属性,该属性使您能够确定是否使用 new 运算符调用函数或构造函数。它允许您检测对象是否是由于构造函数调用而创建的。 ```typescript class Parent { constructor() { console.log(new.target); // 记录用于创建实例的构造函数 } } class Child extends Parent { constructor() { super(); } } const parentX = new Parent(); // [Function: Parent] const child = new Child(); // [Function: Child] ``` ### 动态导入表达式 可以使用 TypeScript 支持的动态导入 ECMAScript 建议有条件地加载模块或按需延迟加载模块。 TypeScript 中动态导入表达式的语法如下: ```typescript async function renderWidget() { const container = document.getElementById('widget'); if (container !== null) { const widget = await import('./widget'); // 动态导入 widget.render(container); } } renderWidget(); ``` ### "tsc –watch" 此命令使用 `--watch` 参数启动 TypeScript 编译器,能够在修改 TypeScript 文件时自动重新编译它们。 ```shell tsc --watch ``` 从 TypeScript 4.9 版本开始,文件监控主要依赖于文件系统事件,如果无法建立基于事件的观察程序,则会自动诉诸轮询。 ### 默认声明 当为变量或参数分配默认值时,将使用默认声明。这意味着如果没有为该变量或参数提供值,则将使用默认值。 ```typescript function greet(name: string = 'Anonymous'): void { console.log(`Hello, ${name}!`); } greet(); // Hello, Anonymous! greet('John'); // Hello, John! ``` ### 可选链 可选的链接运算符 `?.` 与常规点运算符 (`.`) 一样用于访问属性或方法。但是,它通过优雅处理 `undefined` 和 `null` 来终止表达式并返回 `undefined`,而不是抛出错误。 ```typescript type Person = { name: string; age?: number; address?: { street?: string; city?: string; }; }; const person: Person = { name: 'John', }; console.log(person.address?.city); // undefined ``` ### 空合并运算符 如果 `??` 左侧是 `null` 或者 `undefined` ,则空合并运算符返回右侧值,否则,它返回左侧值。 ```typescript const foo = null ?? 'foo'; console.log(foo); // foo const baz = 1 ?? 'baz'; const baz2 = 0 ?? 'baz'; console.log(baz); // 1 console.log(baz2); // 0 ``` ### 模板字符串类型 模板字符串类型允许在类型级别操作字符串值并基于现有字符串生成新的字符串类型。它们对于从基于字符串的操作创建更具表现力和更精确的类型非常有用。 ```typescript type Department = 'enginnering' | 'hr'; type Language = 'english' | 'spanish'; type Id = `${Department}-${Language}-id`; // "enginnering-english-id" | "enginnering-spanish-id" | "hr-english-id" | "hr-spanish-id" ``` ### 函数重载 函数重载允许您为同一函数名定义多个函数签名,每个函数签名具有不同的参数类型和返回类型。当您调用重载函数时,TypeScript 使用提供的参数来确定正确的函数签名: ```typescript function makeGreeting(name: string): string; function makeGreeting(names: string[]): string[]; function makeGreeting(person: unknown): unknown { if (typeof person === 'string') { return `Hi ${person}!`; } else if (Array.isArray(person)) { return person.map(name => `Hi, ${name}!`); } throw new Error('Unable to greet'); } makeGreeting('Simon'); makeGreeting(['Simone', 'John']); ``` ### 递归类型 递归类型是可以引用自身的类型。 这对于定义具有分层或递归结构(可能无限嵌套)的数据结构非常有用,例如链表、树和图。 ```typescript type ListNode = { data: T; next: ListNode | undefined; }; ``` ### 递归条件类型 可以使用 TypeScript 中的逻辑和递归来定义复杂的类型关系。让我们简单地分解一下: 条件类型:允许您基于布尔条件定义类型: ```typescript type CheckNumber = T extends number ? 'Number' : 'Not a number'; type A = CheckNumber<123>; // 'Number' type B = CheckNumber<'abc'>; // 'Not a number' ``` 递归:是指在自己的定义中引用自身的类型定义: ```typescript type Json = string | number | boolean | null | Json[] | { [key: string]: Json }; const data: Json = { prop1: true, prop2: 'prop2', prop3: { prop4: [], }, }; ``` 递归条件类型结合了条件逻辑和递归。这意味着类型定义可以通过条件逻辑依赖于自身,从而创建复杂且灵活的类型关系。 ```typescript type Flatten = T extends Array ? Flatten : T; type NestedArray = [1, [2, [3, 4], 5], 6]; type FlattenedArray = Flatten; // 2 | 3 | 4 | 5 | 1 | 6 ``` ### Node 中的 ECMAScript 模块支持 Node.js 从 15.3.0 版本开始添加了对 ECMAScript 模块的支持,而 TypeScript 从 4.7 版本开始增加了对 Node.js 的 ECMAScript 模块支持。可以通过将 `tsconfig.json` 文件中的`module`属性的值设置为 `nodenext` 来启用此支持。这是一个例子: ```json { "compilerOptions": { "module": "nodenext", "outDir": "./lib", "declaration": true } } ``` Node.js 支持两种模块文件扩展名:`.mjs` 的ES 模块和 `.cjs` 的CommonJS 模块。TypeScript 中的等效文件扩展名适用 `.mts` 于 ES 模块和 `.cts` 于CommonJS 模块。当 TypeScript 编译器将这些文件转译为 JavaScript 时,它将分别创建 `.mjs` 和 `.cjs` 文件。 如果您想在项目中使用 ES 模块,可以type在 package.json 文件中将该属性设置为"module"。这指示 Node.js 将项目视为 ES 模块项目。 此外,TypeScript 还支持 .d.ts 文件中的类型声明。这些声明文件为用 TypeScript 编写的库或模块提供类型信息,允许其他开发人员通过 TypeScript 的类型检查和自动完成功能来利用它们。 ### 断言函数 在 TypeScript 中,断言函数是根据返回值指示特定条件验证的函数。在最简单的形式中,断言函数检查提供的谓词,并在谓词计算结果为 false 时引发错误。 ```typescript function isNumber(value: unknown): asserts value is number { if (typeof value !== 'number') { throw new Error('Not a number'); } } ``` 或者可以声明为函数表达式: ```typescript type AssertIsNumber = (value: unknown) => asserts value is number; const isNumber: AssertIsNumber = value => { if (typeof value !== 'number') { throw new Error('Not a number'); } }; ``` 断言函数与类型保护有相似之处。类型保护最初是为了执行运行时检查并确保值的类型在特定范围内而引入的。具体来说,类型保护是一个计算类型谓词并返回指示谓词是真还是假的布尔值的函数。这与断言函数略有不同,断言函数的目的是在不满足谓词时抛出错误而不是返回 false。 类型保护示例: ```typescript const isNumber = (value: unknown): value is number => typeof value === 'number'; ``` ### 可变参数元组类型 可变元组类型是 TypeScript 4.0 版本中引入的一个功能,让我们通过回顾什么是元组来开始学习它们: 元组类型是一个具有定义长度的数组,并且每个元素的类型已知: ```typescript type Student = [string, number]; const [name, age]: Student = ['Simone', 20]; ``` 术语"可变参数"意味着不定数量(接受可变数量的参数)。 可变参数元组是一种元组类型,它具有以前的所有属性,但确切的形状尚未定义: ```typescript type Bar = [boolean, ...T, number]; type A = Bar<[boolean]>; // [boolean, boolean, number] type B = Bar<['a', 'b']>; // [boolean, 'a', 'b', number] type C = Bar<[]>; // [boolean, number] ``` 在前面的代码中我们可以看到元组形状是由T传入的泛型定义的。 可变参数元组可以接受多个泛型,这使得它们非常灵活: ```typescript type Bar = [...T, boolean, ...G]; type A = Bar<[number], [string]>; // [number, boolean, string] type B = Bar<['a', 'b'], [boolean]>; // ["a", "b", boolean, boolean] ``` 使用新的可变参数元组,我们可以使用: * 元组类型语法中的扩展现在可以是通用的,因此即使我们不知道我们正在操作的实际类型,我们也可以表示元组和数组上的高阶操作 * 其余元素可以出现在元组中的任何位置。 例子: ```typescript type Items = readonly unknown[]; function concat( arr1: T, arr2: U ): [...T, ...U] { return [...arr1, ...arr2]; } concat([1, 2, 3], ['4', '5', '6']); // [1, 2, 3, "4", "5", "6"] ``` ### 装箱类型 装箱类型是指用于将基本类型表示为对象的包装对象。这些包装器对象提供了原始值无法直接使用的附加功能和方法。 当你访问原始 `string` 上的 `charAt` 或者 `normalize` 方法时,JavaScript 将其包装在 `String` 类型的对象中,调用该方法,然后丢弃该对象 示范: ```typescript const originalNormalize = String.prototype.normalize; String.prototype.normalize = function () { console.log(this, typeof this); return originalNormalize.call(this); }; console.log('\u0041'.normalize()); ``` TypeScript 通过为原语及其相应的对象包装器提供单独的类型来表示这种区别: * string => String * number => Number * boolean => Boolean * symbol => Symbol * bigint => BigInt 通常不需要盒装类型。避免使用装箱类型,而是使用基元类型,例如 `string` 代替 `String`。 ### TypeScript 中的协变和逆变 协变和逆变用于描述在处理类型的继承或赋值时关系如何工作。 协变意味着类型关系保留继承或赋值的方向,因此如果类型 A 是类型 B 的子类型,则类型 A 的数组也被视为类型 B 的数组的子类型。这里需要注意的重要事项是维持子类型关系,这意味着协变接受子类型但不接受超类型。 逆变意味着类型关系颠倒了继承或赋值的方向,因此如果类型 A 是类型 B 的子类型,则类型 B 的数组被视为类型 A 数组的子类型。子类型关系颠倒了,这意味着该逆变接受超类型但不接受子类型。 注意:双变量意味着同时接受超类型和子类型。 示例:假设我们有一个适合所有动物的空间和一个专门适合狗的单独空间。 在协方差中,您可以将所有狗放入动物空间中,因为狗是一种动物。但你不能把所有的动物都放在狗的空间里,因为可能还有其他动物混在一起。 在逆变中,您不能将所有动物放入狗空间中,因为动物空间也可能包含其他动物。然而,你可以把所有的狗都放在动物空间里,因为所有的狗也是动物。 ```typescript // 协变示例 class Animal { name: string; constructor(name: string) { this.name = name; } } class Dog extends Animal { breed: string; constructor(name: string, breed: string) { super(name); this.breed = breed; } } let animals: Animal[] = []; let dogs: Dog[] = []; // 协变允许将子类型(狗)数组分配给超类型(动物)数组 animals = dogs; dogs = animals; // 无效: 'Animal[]' 不能赋值给 'Dog[]' // 逆变示例 type Feed = (animal: T) => void; let feedAnimal: Feed = (animal: Animal) => { console.log(`Animal name: ${animal.name}`); }; let feedDog: Feed = (dog: Dog) => { console.log(`Dog name: ${dog.name}, Breed: ${dog.breed}`); }; // 逆变允许将超类型(动物)回调赋值给子类型(狗)回调 feedDog = feedAnimal; feedAnimal = feedDog; // 无效: Type 'Feed' 不能赋值给 'Feed'. ``` 在 TypeScript 中,数组的类型关系是协变的,而函数参数的类型关系是逆变的。这意味着 TypeScript 同时表现出协变和逆变,具体取决于上下文。 #### 类型参数的可选方差注释 从 TypeScript 4.7.0 开始,我们可以使用out和in关键字来具体说明方差注释。 对于协变,使用out关键字: ```typescript type AnimalCallback = () => T; // 此处 T 是协变的 ``` 对于逆变,使用in关键字: ```typescript type AnimalCallback = (value: T) => void; // 此处 T 是逆变的 ``` ### 模板字符串模式索引签名 模板字符串模式索引签名允许我们使用模板字符串模式定义灵活的索引签名。 此功能使我们能够创建可以使用特定字符串键模式进行索引的对象,从而在访问和操作属性时提供更多控制和特异性。 TypeScript 4.4 版开始允许符号和模板字符串模式的索引签名。 ```typescript const uniqueSymbol = Symbol('description'); type MyKeys = `key-${string}`; type MyObject = { [uniqueSymbol]: string; [key: MyKeys]: number; }; const obj: MyObject = { [uniqueSymbol]: 'Unique symbol key', 'key-a': 123, 'key-b': 456, }; console.log(obj[uniqueSymbol]); // Unique symbol key console.log(obj['key-a']); // 123 console.log(obj['key-b']); // 456 ``` ### satisfies操作符 `satisfies` 允许您检查给定类型是否满足特定接口或条件。换句话说,它确保类型具有特定接口所需的所有属性和方法。这是确保变量适合类型定义的一种方法。 下面是一个示例: ```typescript type Columns = 'name' | 'nickName' | 'attributes'; type User = Record; // `User`的类型注释 const user: User = { name: 'Simone', nickName: undefined, attributes: ['dev', 'admin'], }; // 在以下几行中,TypeScript 将无法正确推断 user.attributes?.map(console.log); // 'string | string[]' 中不存在属性 'map'。'string' 中不存在属性 'map'。 user.nickName; // string | string[] | undefined // 类型断言 `as` const user2 = { name: 'Simon', nickName: undefined, attributes: ['dev', 'admin'], } as User; // 这里也一样的, TypeScript 将无法正确推断 user2.attributes?.map(console.log); //'string | string[]' 中不存在属性 'map'。'string' 中不存在属性 'map'。 user2.nickName; // string | string[] | undefined // 使用"satisfies"运算符我们现在可以正确推断类型 const user3 = { name: 'Simon', nickName: undefined, attributes: ['dev', 'admin'], } satisfies User; user3.attributes?.map(console.log); // TypeScript 推断正确: string[] user3.nickName; // TypeScript 推断正确: undefined ``` ### 仅类型导入和导出 仅类型导入和导出允许您导入或导出类型,而无需导入或导出与这些类型关联的值或函数。 这对于减小捆绑包的大小很有用。 要使用仅类型导入,您可以使用`import type`关键字。 TypeScript 允许在仅类型导入中使用声明和实现文件扩展名(.ts、.mts、.cts 和 .tsx),无论`allowImportingTsExtensions`设置如何。 例如: ```typescript import type { House } from './house.ts'; ``` 以下是支持的形式: ```typescript import type T from './mod'; import type { A, B } from './mod'; import type * as Types from './mod'; export type { T }; export type { T } from './mod'; ``` ### 使用声明和显式资源管理 "using"声明是块范围的、不可变的绑定,类似于"const",用于管理一次性资源。 当使用值初始化时,该值的"Symbol.dispose"方法将被记录,并随后在退出封闭块作用域时执行。 这是基于 ECMAScript 的资源管理功能,该功能对于在对象创建后执行基本的清理任务非常有用,例如关闭连接、删除文件和释放内存。 笔记: * 由于最近在 TypeScript 5.2 版中引入,大多数运行时缺乏本机支持。 您将需要以下功能的填充:`Symbol.dispose`、`Symbol.asyncDispose`、`DisposableStack`、`AsyncDisposableStack`、`SuppressedError`。 * 此外,您需要按如下方式配置 tsconfig.json: ```json { "compilerOptions": { "target": "es2022", "lib": ["es2022", "esnext.disposable", "dom"] } } ```` 例子: ```typescript //@ts-ignore Symbol.dispose ??= Symbol('Symbol.dispose'); // 简单的兼容性填充 const doWork = (): Disposable => { return { [Symbol.dispose]: () => { console.log('disposed'); }, }; }; console.log(1); { using work = doWork(); // 资源被声明 console.log(2); } // 资源被释放 (例如, `work[Symbol.dispose]()` 被执行) console.log(3); ``` 该代码将记录: ```shell 1 2 disposed 3 ``` 符合处置条件的资源必须遵守 `Disposable` 接口: ```typescript // lib.esnext.disposable.d.ts interface Disposable { [Symbol.dispose](): void; } ``` "using"声明在堆栈中记录资源处置操作,确保它们以与声明相反的顺序处置: ```typescript { using j = getA(), y = getB(); using k = getC(); } // 先释放 `C`, 然后 `B`, 然后 `A`. ``` 即使发生后续代码或异常,也保证会释放资源。 这可能会导致处置可能引发异常,并可能抑制另一个异常。 为了保留有关被抑制错误的信息,引入了一个新的本机异常"SuppressedError"。 #### 使用声明等待 "await using"声明处理异步一次性资源。 该值必须具有"Symbol.asyncDispose"方法,该方法将在块末尾等待。 ```typescript async function doWorkAsync() { await using work = doWorkAsync(); // 资源被声明 } // // 资源被释放 (例如, `await work[Symbol.asyncDispose]()` 被执行) ``` 对于异步可处置资源,它必须遵守"Disposable"或"AsyncDisposable"接口: ```typescript // lib.esnext.disposable.d.ts interface AsyncDisposable { [Symbol.asyncDispose](): Promise; } ``` ```typescript //@ts-ignore Symbol.asyncDispose ??= Symbol('Symbol.asyncDispose'); // Simple polify class DatabaseConnection implements AsyncDisposable { // 当对象被异步释放时会被调用的方法 [Symbol.asyncDispose]() { // Close the connection and return a promise return this.close(); } async close() { console.log('Closing the connection...'); await new Promise(resolve => setTimeout(resolve, 1000)); console.log('Connection closed.'); } } async function doWork() { // 创建一个新的连接,并在其超出作用域时进行异步释放 await using connection = new DatabaseConnection(); // 资源被声明 console.log('Doing some work...'); } // 资源被释放 (例如, `await connection[Symbol.asyncDispose]()` 被执行) doWork(); ``` 代码日志: ```shell Doing some work... Closing the connection... Connection closed. ``` 语句中允许使用"using"和"await using"声明:"for"、"for-in"、"for-of"、"for-await-of"、"switch"。 ### 导入属性 TypeScript 5.3 的导入属性(导入标签)告诉运行时如何处理模块(JSON 等)。这通过确保干净的导入来提高安全性,并与内容安全策略 (CSP) 保持一致,以实现更安全的资源加载。TypeScript 确保它们有效,但让运行时处理它们的解释以进行特定的模块处理。 示例: ```typescript import config from './config.json' with { type: 'json' }; ``` 使用动态导入: ```typescript const config = import('./config.json', { with: { type: 'json' } }); ``` ================================================ FILE: README.md ================================================ # The Concise TypeScript Book The Concise TypeScript Book provides a comprehensive and succinct overview of TypeScript's capabilities. It offers clear explanations covering all aspects found in the latest version of the language, from its powerful type system to advanced features. Whether you're a beginner or an experienced developer, this book is an invaluable resource to enhance your understanding and proficiency in TypeScript. This book is completely Free and Open Source. I believe that high-quality technical education should be accessible to everyone, which is why I keep this book free and open. If the book helped you squash a bug, understand a tricky concept, or advance in your career, please consider supporting my work by paying what you want (suggested price: 15 USD) or sponsoring a coffee. Your support helps me keep the content up to date and expand it with new examples and deeper explanations. [![Buy Me a Coffee](https://img.shields.io/badge/buy_me_a_coffee-FFDD00?style=for-the-badge&logo=buy-me-a-coffee&logoColor=black)](https://www.buymeacoffee.com/simonepoggiali) [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/donate/?business=QW82ZS956XLFY&no_recurring=0¤cy_code=EUR) ## Translations This book has been translated into several language versions, including: [Chinese](https://github.com/gibbok/typescript-book/blob/main/README-zh_CN.md) [Italian](https://github.com/gibbok/typescript-book/blob/main/README-it_IT.md) [Portuguese (Brazil)](https://github.com/gibbok/typescript-book/blob/main/README-pt_BR.md) [Swedish](https://github.com/gibbok/typescript-book/blob/main/README-sv_SE.md) ## Downloads and website You can also download the Epub version: [https://github.com/gibbok/typescript-book/tree/main/downloads](https://github.com/gibbok/typescript-book/tree/main/downloads) An online version is available at: [https://gibbok.github.io/typescript-book](https://gibbok.github.io/typescript-book) ## Table of Contents - [The Concise TypeScript Book](#the-concise-typescript-book) - [Translations](#translations) - [Downloads and website](#downloads-and-website) - [Table of Contents](#table-of-contents) - [Introduction](#introduction) - [About the Author](#about-the-author) - [TypeScript Introduction](#typescript-introduction) - [What is TypeScript?](#what-is-typescript) - [Why TypeScript?](#why-typescript) - [TypeScript and JavaScript](#typescript-and-javascript) - [TypeScript Code Generation](#typescript-code-generation) - [Modern JavaScript Now (Downleveling)](#modern-javascript-now-downleveling) - [Getting Started With TypeScript](#getting-started-with-typescript) - [Installation](#installation) - [Configuration](#configuration) - [TypeScript Configuration File](#typescript-configuration-file) - [target](#target) - [lib](#lib) - [strict](#strict) - [module](#module) - [moduleResolution](#moduleresolution) - [esModuleInterop](#esmoduleinterop) - [jsx](#jsx) - [skipLibCheck](#skiplibcheck) - [files](#files) - [include](#include) - [exclude](#exclude) - [importHelpers](#importhelpers) - [Migration to TypeScript Advice](#migration-to-typescript-advice) - [Exploring the Type System](#exploring-the-type-system) - [The TypeScript Language Service](#the-typescript-language-service) - [Structural Typing](#structural-typing) - [TypeScript Fundamental Comparison Rules](#typescript-fundamental-comparison-rules) - [Types as Sets](#types-as-sets) - [Assign a type: Type Declarations and Type Assertions](#assign-a-type-type-declarations-and-type-assertions) - [Type Declaration](#type-declaration) - [Type Assertion](#type-assertion) - [Ambient Declarations](#ambient-declarations) - [Property Checking and Excess Property Checking](#property-checking-and-excess-property-checking) - [Weak Types](#weak-types) - [Strict Object Literal Checking (Freshness)](#strict-object-literal-checking-freshness) - [Type Inference](#type-inference) - [More Advanced Inferences](#more-advanced-inferences) - [Type Widening](#type-widening) - [Const](#const) - [Const Modifier on Type Parameters](#const-modifier-on-type-parameters) - [Const assertion](#const-assertion) - [Explicit Type Annotation](#explicit-type-annotation) - [Type Narrowing](#type-narrowing) - [Conditions](#conditions) - [Throwing or returning](#throwing-or-returning) - [Discriminated Union](#discriminated-union) - [User-Defined Type Guards](#user-defined-type-guards) - [Primitive Types](#primitive-types) - [string](#string) - [boolean](#boolean) - [number](#number) - [bigInt](#bigint) - [Symbol](#symbol) - [null and undefined](#null-and-undefined) - [Array](#array) - [any](#any) - [Type Annotations](#type-annotations) - [Optional Properties](#optional-properties) - [Readonly Properties](#readonly-properties) - [Index Signatures](#index-signatures) - [Extending Types](#extending-types) - [Literal Types](#literal-types) - [Literal Inference](#literal-inference) - [strictNullChecks](#strictnullchecks) - [Enums](#enums) - [Numeric enums](#numeric-enums) - [String enums](#string-enums) - [Constant enums](#constant-enums) - [Reverse mapping](#reverse-mapping) - [Ambient enums](#ambient-enums) - [Computed and constant members](#computed-and-constant-members) - [Narrowing](#narrowing) - [typeof type guards](#typeof-type-guards) - [Truthiness narrowing](#truthiness-narrowing) - [Equality narrowing](#equality-narrowing) - [In Operator narrowing](#in-operator-narrowing) - [instanceof narrowing](#instanceof-narrowing) - [Assignments](#assignments) - [Control Flow Analysis](#control-flow-analysis) - [Type Predicates](#type-predicates) - [Discriminated Unions](#discriminated-unions) - [The never Type](#the-never-type) - [Exhaustiveness checking](#exhaustiveness-checking) - [Object Types](#object-types) - [Tuple Type (Anonymous)](#tuple-type-anonymous) - [Named Tuple Type (Labeled)](#named-tuple-type-labeled) - [Fixed Length Tuple](#fixed-length-tuple) - [Union Type](#union-type) - [Intersection Types](#intersection-types) - [Type Indexing](#type-indexing) - [Type from Value](#type-from-value) - [Type from Func Return](#type-from-func-return) - [Type from Module](#type-from-module) - [Mapped Types](#mapped-types) - [Mapped Type Modifiers](#mapped-type-modifiers) - [Conditional Types](#conditional-types) - [Distributive Conditional Types](#distributive-conditional-types) - [infer Type Inference in Conditional Types](#infer-type-inference-in-conditional-types) - [Predefined Conditional Types](#predefined-conditional-types) - [Template Union Types](#template-union-types) - [Any type](#any-type) - [Unknown type](#unknown-type) - [Void type](#void-type) - [Never type](#never-type) - [Interface and Type](#interface-and-type) - [Common Syntax](#common-syntax) - [Basic Types](#basic-types) - [Objects and Interfaces](#objects-and-interfaces) - [Union and Intersection Types](#union-and-intersection-types) - [Built-in Type Primitives](#built-in-type-primitives) - [Common Built-in JS Objects](#common-built-in-js-objects) - [Overloads](#overloads) - [Merging and Extension](#merging-and-extension) - [Differences between Type and Interface](#differences-between-type-and-interface) - [Class](#class) - [Class Common Syntax](#class-common-syntax) - [Constructor](#constructor) - [Private and Protected Constructors](#private-and-protected-constructors) - [Access Modifiers](#access-modifiers) - [Get and Set](#get-and-set) - [Auto-Accessors in Classes](#auto-accessors-in-classes) - [this](#this) - [Parameter Properties](#parameter-properties) - [Abstract Classes](#abstract-classes) - [With Generics](#with-generics) - [Decorators](#decorators) - [Class Decorators](#class-decorators) - [Property Decorator](#property-decorator) - [Method Decorator](#method-decorator) - [Getter and Setter Decorators](#getter-and-setter-decorators) - [Decorator Metadata](#decorator-metadata) - [Inheritance](#inheritance) - [Statics](#statics) - [Property initialization](#property-initialization) - [Method overloading](#method-overloading) - [Generics](#generics) - [Generic Type](#generic-type) - [Generic Classes](#generic-classes) - [Generic Constraints](#generic-constraints) - [Generic contextual narrowing](#generic-contextual-narrowing) - [Erased Structural Types](#erased-structural-types) - [Namespacing](#namespacing) - [Symbols](#symbols) - [Triple-Slash Directives](#triple-slash-directives) - [Type Manipulation](#type-manipulation) - [Creating Types from Types](#creating-types-from-types) - [Indexed Access Types](#indexed-access-types) - [Utility Types](#utility-types) - [Awaited\](#awaitedt) - [Partial\](#partialt) - [Required\](#requiredt) - [Readonly\](#readonlyt) - [Record\](#recordk-t) - [Pick\](#pickt-k) - [Omit\](#omitt-k) - [Exclude\](#excludet-u) - [Extract\](#extractt-u) - [NonNullable\](#nonnullablet) - [Parameters\](#parameterst) - [ConstructorParameters\](#constructorparameterst) - [ReturnType\](#returntypet) - [InstanceType\](#instancetypet) - [ThisParameterType\](#thisparametertypet) - [OmitThisParameter\](#omitthisparametert) - [ThisType\](#thistypet) - [Uppercase\](#uppercaset) - [Lowercase\](#lowercaset) - [Capitalize\](#capitalizet) - [Uncapitalize\](#uncapitalizet) - [NoInfer\](#noinfert) - [Others](#others) - [Errors and Exception Handling](#errors-and-exception-handling) - [Mixin classes](#mixin-classes) - [Asynchronous Language Features](#asynchronous-language-features) - [Iterators and Generators](#iterators-and-generators) - [TsDocs JSDoc Reference](#tsdocs-jsdoc-reference) - [@types](#types) - [JSX](#jsx-1) - [ES6 Modules](#es6-modules) - [ES7 Exponentiation Operator](#es7-exponentiation-operator) - [The for-await-of Statement](#the-for-await-of-statement) - [New target meta-property](#new-target-meta-property) - [Dynamic Import Expressions](#dynamic-import-expressions) - ["tsc –watch"](#tsc-watch) - [Non-null Assertion Operator](#non-null-assertion-operator) - [Defaulted declarations](#defaulted-declarations) - [Optional Chaining](#optional-chaining) - [Nullish coalescing operator](#nullish-coalescing-operator) - [Template Literal Types](#template-literal-types) - [Function overloading](#function-overloading) - [Recursive Types](#recursive-types) - [Recursive Conditional Types](#recursive-conditional-types) - [ECMAScript Module Support in Node](#ecmascript-module-support-in-node) - [Assertion Functions](#assertion-functions) - [Variadic Tuple Types](#variadic-tuple-types) - [Boxed types](#boxed-types) - [Covariance and Contravariance in TypeScript](#covariance-and-contravariance-in-typescript) - [Optional Variance Annotations for Type Parameters](#optional-variance-annotations-for-type-parameters) - [Template String Pattern Index Signatures](#template-string-pattern-index-signatures) - [The satisfies Operator](#the-satisfies-operator) - [Type-Only Imports and Export](#type-only-imports-and-export) - [using declaration and Explicit Resource Management](#using-declaration-and-explicit-resource-management) - [await using declaration](#await-using-declaration) - [Import Attributes](#import-attributes) ## Introduction Welcome to The Concise TypeScript Book! This guide equips you with essential knowledge and practical skills for effective TypeScript development. Discover key concepts and techniques to write clean, robust code. Whether you're a beginner or an experienced developer, this book serves as both a comprehensive guide and a handy reference for leveraging TypeScript's power in your projects. This book covers TypeScript 5.2. ## About the Author Simone Poggiali is an experienced Staff Engineer with a passion for writing professional-grade code since the 90s. Throughout his international career, he has contributed to numerous projects for a wide range of clients, from startups to large organizations. Notable companies such as HelloFresh, Siemens, O2, Leroy Merlin and Snowplow have benefited from his expertise and dedication. You can reach Simone Poggiali on the following platforms: * LinkedIn: [https://www.linkedin.com/in/simone-poggiali](https://www.linkedin.com/in/simone-poggiali) * GitHub: [https://github.com/gibbok](https://github.com/gibbok) * X.com: [https://x.com/gibbok_coding](https://x.com/gibbok_coding) * Email: gibbok.coding📧gmail.com Full list of contributors: [https://github.com/gibbok/typescript-book/graphs/contributors](https://github.com/gibbok/typescript-book/graphs/contributors) ## TypeScript Introduction ### What is TypeScript? TypeScript is a strongly typed programming language that builds on JavaScript. It was originally designed by Anders Hejlsberg in 2012 and is currently developed and maintained by Microsoft as an open source project. TypeScript compiles to JavaScript and can be executed in any JavaScript runtime (e.g., a browser or Node.js on a server). It supports multiple programming paradigms such as functional, generic, imperative, and object-oriented programming, and is a compiled (transpiled) language that is converted into JavaScript before execution. ### Why TypeScript? TypeScript is a strongly typed language that helps prevent common programming mistakes and avoid certain kinds of run-time errors before the program is executed. A strongly typed language allows the developer to specify various program constraints and behaviors in the data type definitions, facilitating the ability to verify the correctness of the software and prevent defects. This is especially valuable in large-scale applications. Some of the benefits of TypeScript: * Static typing, optionally strongly typed * Type Inference * Access to ES6 and ES7 features * Cross-Platform and Cross-browser Compatibility * Tooling support with IntelliSense ### TypeScript and JavaScript TypeScript is written in `.ts` or `.tsx` files, while JavaScript files are written in `.js` or `.jsx`. Files with the extension `.tsx` or `.jsx` can contain JavaScript Syntax Extension JSX, which is used in React for UI development. TypeScript is a typed superset of JavaScript (ECMAScript 2015) in terms of syntax. All JavaScript code is valid TypeScript code, but the reverse is not always true. For instance, consider a function in a JavaScript file with the `.js` extension, such as the following: ```typescript const sum = (a, b) => a + b; ``` The function can be converted and used in TypeScript by changing the file extension to `.ts`. However, if the same function is annotated with TypeScript types, it cannot be executed in any JavaScript runtime without compilation. The following TypeScript code will produce a syntax error if it is not compiled: ```typescript const sum = (a: number, b: number): number => a + b; ``` TypeScript was designed to detect possible exceptions that can occur at runtime during compilation time by having the developer define the intent with type annotations. In addition, TypeScript can also catch issues if no type annotation is provided. For instance, the following code snippet does not specify any TypeScript types: ```typescript const items = [{ x: 1 }, { x: 2 }]; const result = items.filter(item => item.y); ``` In this case, TypeScript detects an error and reports: ```text Property 'y' does not exist on type '{ x: number; }'. ``` TypeScript's type system is largely influenced by the runtime behavior of JavaScript. For example, the addition operator (+), which in JavaScript can either perform string concatenation or numeric addition, is modeled in the same way in TypeScript: ```typescript const result = '1' + 1; // Result is of type string ``` The team behind TypeScript has made a deliberate decision to flag unusual usage of JavaScript as errors. For instance, consider the following valid JavaScript code: ```typescript const result = 1 + true; // In JavaScript, the result is equal 2 ``` However, TypeScript throws an error: ```text Operator '+' cannot be applied to types 'number' and 'boolean'. ``` This error occurs because TypeScript strictly enforces type compatibility, and in this case, it identifies an invalid operation between a number and a boolean. ### TypeScript Code Generation The TypeScript compiler has two main responsibilities: checking for type errors and compiling to JavaScript. These two processes are independent of each other. Types do not affect the execution of the code in a JavaScript runtime, as they are completely erased during compilation. TypeScript can still output JavaScript even in the presence of type errors. Here is an example of TypeScript code with a type error: ```typescript const add = (a: number, b: number): number => a + b; const result = add('x', 'y'); // Argument of type 'string' is not assignable to parameter of type 'number'. ``` However, it can still produce executable JavaScript output: ```typescript 'use strict'; const add = (a, b) => a + b; const result = add('x', 'y'); // xy ``` It is not possible to check TypeScript types at runtime. For example: ```typescript interface Animal { name: string; } interface Dog extends Animal { bark: () => void; } interface Cat extends Animal { meow: () => void; } const makeNoise = (animal: Animal) => { if (animal instanceof Dog) { // 'Dog' only refers to a type, but is being used as a value here. // ... } }; ``` As the types are erased after compilation, there is no way to run this code in JavaScript. To recognize types at runtime, we need to use another mechanism. TypeScript provides several options, with a common one being "tagged union". For example: ```typescript interface Dog { kind: 'dog'; // Tagged union bark: () => void; } interface Cat { kind: 'cat'; // Tagged union meow: () => void; } type Animal = Dog | Cat; const makeNoise = (animal: Animal) => { if (animal.kind === 'dog') { animal.bark(); } else { animal.meow(); } }; const dog: Dog = { kind: 'dog', bark: () => console.log('bark'), }; makeNoise(dog); ``` The property "kind" is a value that can be used at runtime to distinguish between objects in JavaScript. It is also possible for a value at runtime to have a type different from the one declared in the type declaration. For instance, if the developer has misinterpreted an API type and annotated it incorrectly. TypeScript is a superset of JavaScript, so the "class" keyword can be used as a type and value at runtime. ```typescript class Animal { constructor(public name: string) {} } class Dog extends Animal { constructor( public name: string, public bark: () => void ) { super(name); } } class Cat extends Animal { constructor( public name: string, public meow: () => void ) { super(name); } } type Mammal = Dog | Cat; const makeNoise = (mammal: Mammal) => { if (mammal instanceof Dog) { mammal.bark(); } else { mammal.meow(); } }; const dog = new Dog('Fido', () => console.log('bark')); makeNoise(dog); ``` In JavaScript, a "class" has a "prototype" property, and the "instanceof" operator can be used to test if the prototype property of a constructor appears anywhere in the prototype chain of an object. TypeScript has no effect on runtime performance, as all types will be erased. However, TypeScript does introduce some build time overhead. ### Modern JavaScript Now (Downleveling) TypeScript can compile code to any released version of JavaScript since ECMAScript 3 (1999). This means that TypeScript can transpile code from the latest JavaScript features to older versions, a process known as Downleveling. This allows the usage of modern JavaScript while maintaining maximum compatibility with older runtime environments. It's important to note that during transpilation to an older version of JavaScript, TypeScript may generate code that could incur a performance overhead compared to native implementations. Here are some of the modern JavaScript features that can be used in TypeScript: * ECMAScript modules instead of AMD-style "define" callbacks or CommonJS "require" statements. * Classes instead of prototypes. * Variables declaration using "let" or "const" instead of "var". * "for-of" loop or ".forEach" instead of the traditional "for" loop. * Arrow functions instead of function expressions. * Destructuring assignment. * Shorthand property/method names and computed property names. * Default function parameters. By leveraging these modern JavaScript features, developers can write more expressive and concise code in TypeScript. ## Getting Started With TypeScript ### Installation Visual Studio Code provides excellent support for the TypeScript language but does not include the TypeScript compiler. To install the TypeScript compiler, you can use a package manager like npm or yarn: ```shell npm install typescript --save-dev ``` or ```shell yarn add typescript --dev ``` Make sure to commit the generated lockfile to ensure that every team member uses the same version of TypeScript. To run the TypeScript compiler, you can use the following commands ```shell npx tsc ``` or ```shell yarn tsc ``` It is recommended to install TypeScript project-wise rather than globally, as it provides a more predictable build process. However, for one-off occasions, you can use the following command: ```shell npx tsc ``` or installing it globally: ```shell npm install -g typescript ``` If you are using Microsoft Visual Studio, you can obtain TypeScript as a package in NuGet for your MSBuild projects. In the NuGet Package Manager Console, run the following command: ```shell Install-Package Microsoft.TypeScript.MSBuild ``` During the TypeScript installation, two executables are installed: "tsc" as the TypeScript compiler and "tsserver" as the TypeScript standalone server. The standalone server contains the compiler and language services that can be utilized by editors and IDEs to provide intelligent code completion. Additionally, there are several TypeScript-compatible transpilers available, such as Babel (via a plugin) or swc. These transpilers can be used to convert TypeScript code into other target languages or versions. ### Configuration TypeScript can be configured using the tsc CLI options or by utilizing a dedicated configuration file called tsconfig.json placed in the root of the project. To generate a tsconfig.json file prepopulated with recommended settings, you can use the following command: ```shell tsc --init ``` When executing the `tsc` command locally, TypeScript will compile the code using the configuration specified in the nearest tsconfig.json file. Here are some examples of CLI commands that run with the default settings: ```shell tsc main.ts // Compile a specific file (main.ts) to JavaScript tsc src/*.ts // Compile any .ts files under the 'src' folder to JavaScript tsc app.ts util.ts --outfile index.js // Compile two TypeScript files (app.ts and util.ts) into a single JavaScript file (index.js) ``` ### TypeScript Configuration File A tsconfig.json file is used to configure the TypeScript Compiler (tsc). Usually, it is added to the root of the project, together with the `package.json` file. Notes: * tsconfig.json accepts comments even if it is in json format. * It is advisable to use this configuration file instead of the command-line options. At the following link you can find the complete documentation and its schema: [https://www.typescriptlang.org/tsconfig](https://www.typescriptlang.org/tsconfig) [https://www.typescriptlang.org/tsconfig/](https://www.typescriptlang.org/tsconfig/) The following represents a list of the common and useful configurations: #### target The "target" property is used to specify which version of JavaScript ECMAScript version your TypeScript should emit/compile into. For modern browsers ES6 is a good option, for older browsers, ES5 is recommended. #### lib The "lib" property is used to specify which library files to include at compilation time. TypeScript automatically includes APIs for features specified in the "target" property, but it is possible to omit or pick specific libraries for particular needs. For instance, if you are working on a server project, you could exclude the "DOM" library, which is useful only in a browser environment. #### strict The "strict" property enables stronger guarantees and enhances type safety. It is advisable to always include this property in your project's tsconfig.json file. Enabling the "strict" property allows TypeScript to: * Emit code using "use strict" for each source file. * Consider "null" and "undefined" in the type checking process. * Disable the usage of the "any" type when no type annotations are present. * Raise an error on the usage of the "this" expression, which would otherwise imply the "any" type. #### module The "module" property sets the module system supported for the compiled program. During runtime, a module loader is used to locate and execute dependencies based on the specified module system. The most common module loaders used in JavaScript are Node.js CommonJS for server-side applications and RequireJS for AMD modules in browser-based web applications. TypeScript can emit code for various module systems, including UMD, System, ESNext, ES2015/ES6, and ES2020. Note: The module system should be chosen based on the target environment and the module loading mechanism available in that environment. #### moduleResolution The "moduleResolution" property specifies the module resolution strategy. Use "node" for modern TypeScript code, the "classic" strategy is used only for old versions of TypeScript (before 1.6). #### esModuleInterop The "esModuleInterop" property allows import default from CommonJS modules that did not export using the "default" property, this property provides a shim to ensure compatibility in the emitted JavaScript. After enabling this option we can use `import MyLibrary from "my-library"` instead of `import * as MyLibrary from "my-library"`. #### jsx The "jsx" property applies only to .tsx files used in ReactJS and controls how JSX constructs are compiled into JavaScript. A common option is "preserve" which will compile to a .jsx file keeping unchanged the JSX so it can be passed to different tools like Babel for further transformations. #### skipLibCheck The "skipLibCheck'' property will prevent TypeScript from type-checking the entire imported third-party packages. This property will reduce the compile time of a project. TypeScript will still check your code against the type definitions provided by these packages. #### files The "files" property indicates to the compiler a list of files that must always be included in the program. #### include The "include" property indicates to the compiler a list of files that we would like to include. This property allows glob-like patterns, such as "\*_" for any subdirectory, "_" for any file name, and "?" for optional characters. #### exclude The "exclude" property indicates to the compiler a list of files that should not be included in the compilation. This can include files such as "node_modules" or test files. Note: tsconfig.json allows comments. ### importHelpers TypeScript uses helper code when generating code for certain advanced or down-leveled JavaScript features. By default, these helpers are duplicated in files using them. The `importHelpers` option imports these helpers from the `tslib` module instead, making the JavaScript output more efficient. ### Migration to TypeScript Advice For large projects, it is recommended to adopt a gradual transition where TypeScript and JavaScript code will initially coexist. Only small projects can be migrated to TypeScript in one go. The first step of this transition is to introduce TypeScript into the build chain process. This can be done by using the "allowJs" compiler option, which permits .ts and .tsx files to coexist with existing JavaScript files. As TypeScript will fall back to a type of "any" for a variable when it cannot infer the type from JavaScript files, it is recommended to disable "noImplicitAny" in your compiler options at the beginning of the migration. The second step is to ensure that your JavaScript tests work alongside TypeScript files so that you can run tests as you convert each module. If you are using Jest, consider using `ts-jest`, which allows you to test TypeScript projects with Jest. The third step is to include type declarations for third-party libraries in your project. These declarations can be found either bundled or on DefinitelyTyped. You can search for them using [https://www.typescriptlang.org/dt/search](https://www.typescriptlang.org/dt/search) and install them using: ```shell npm install --save-dev @types/package-name ``` or ```shell yarn add --dev @types/package-name ``` The fourth step is to migrate module by module with a bottom-up approach, following your Dependency Graph starting with the leaves. The idea is to start converting Modules that do not depend on other Modules. To visualize the dependency graphs, you can use the "madge" tool. Good candidate modules for these initial conversions are utility functions and code related to external APIs or specifications. It is possible to automatically generate TypeScript type definitions from Swagger contracts, GraphQL or JSON schemas to be included in your project. When there are no specifications or official schemas available, you can generate types from raw data, such as JSON returned by a server. However, it is recommended to generate types from specifications instead of data to avoid missing edge cases. During the migration, refrain from code refactoring and focus only on adding types to your modules. The fifth step is to enable "noImplicitAny," which will enforce that all types are known and defined, providing a better TypeScript experience for your project. During the migration, you can use the `@ts-check` directive, which enables TypeScript type checking in a JavaScript file. This directive provides a loose version of type checking and can be initially used to identify issues in JavaScript files. When `@ts-check` is included in a file, TypeScript will try to deduce definitions using JSDoc-style comments. However, consider using JSDoc annotations only at a very early stage of the migration. Consider keeping the default value of `noEmitOnError` in your tsconfig.json as false. This will allow you to output JavaScript source code even if errors are reported. ## Exploring the Type System ### The TypeScript Language Service The TypeScript Language Service, also known as tsserver, offers various features such as error reporting, diagnostics, compile-on-save, renaming, go to definition, completion lists, signature help, and more. It is primarily used by integrated development environments (IDEs) to provide IntelliSense support. It seamlessly integrates with Visual Studio Code and is utilized by tools like Conquer of Completion (Coc). Developers can leverage a dedicated API and create their own custom language service plugins to enhance the TypeScript editing experience. This can be particularly useful for implementing special linting features or enabling auto-completion for a custom templating language. An example of a real-world custom plugin is "typescript-styled-plugin", which provides syntax error reporting and IntelliSense support for CSS properties in styled components. For more information and quick start guides, you can refer to the official TypeScript Wiki on GitHub: [https://github.com/microsoft/TypeScript/wiki/](https://github.com/microsoft/TypeScript/wiki/) ### Structural Typing TypeScript is based on a structural type system. This means that the compatibility and equivalence of types are determined by the type's actual structure or definition, rather than its name or place of declaration, as in nominative type systems like C# or C. TypeScript's structural type system was designed based on how JavaScript's dynamic duck typing system works during runtime. The following example is valid TypeScript code. As you can observe, "X" and "Y" have the same member "a," even though they have different declaration names. The types are determined by their structures, and in this case, since the structures are the same, they are compatible and valid. ```typescript type X = { a: string; }; type Y = { a: string; }; const x: X = { a: 'a' }; const y: Y = x; // Valid ``` ### TypeScript Fundamental Comparison Rules The TypeScript comparison process is recursive and executed on types nested at any level. A type "X" is compatible with "Y" if "Y" has at least the same members as "X". ```typescript type X = { a: string; }; const y = { a: 'A', b: 'B' }; // Valid, as it has at least the same members as X const r: X = y; ``` Function parameters are compared by types, not by their names: ```typescript type X = (a: number) => void; type Y = (a: number) => void; let x: X = (j: number) => undefined; let y: Y = (k: number) => undefined; y = x; // Valid x = y; // Valid ``` Function return types must be the same: ```typescript type X = (a: number) => undefined; type Y = (a: number) => number; let x: X = (a: number) => undefined; let y: Y = (a: number) => 1; y = x; // Invalid x = y; // Invalid ``` The return type of a source function must be a subtype of the return type of a target function: ```typescript let x = () => ({ a: 'A' }); let y = () => ({ a: 'A', b: 'B' }); x = y; // Valid y = x; // Invalid member b is missing ``` Discarding function parameters is allowed, as it is a common practice in JavaScript, for instance using "Array.prototype.map()": ```typescript [1, 2, 3].map((element, _index, _array) => element + 'x'); ``` Therefore, the following type declarations are completely valid: ```typescript type X = (a: number) => undefined; type Y = (a: number, b: number) => undefined; let x: X = (a: number) => undefined; let y: Y = (a: number) => undefined; // Missing b parameter y = x; // Valid ``` Any additional optional parameters of the source type are valid: ```typescript type X = (a: number, b?: number, c?: number) => undefined; type Y = (a: number) => undefined; let x: X = a => undefined; let y: Y = a => undefined; y = x; // Valid x = y; //Valid ``` Any optional parameters of the target type without corresponding parameters in the source type are valid and not an error: ```typescript type X = (a: number) => undefined; type Y = (a: number, b?: number) => undefined; let x: X = a => undefined; let y: Y = a => undefined; y = x; // Valid x = y; // Valid ``` The rest parameter is treated as an infinite series of optional parameters: ```typescript type X = (a: number, ...rest: number[]) => undefined; let x: X = a => undefined; //valid ``` Functions with overloads are valid if the overload signature is compatible with its implementation signature: ```typescript function x(a: string): void; function x(a: string, b: number): void; function x(a: string, b?: number): void { console.log(a, b); } x('a'); // Valid x('a', 1); // Valid function y(a: string): void; // Invalid, not compatible with implementation signature function y(a: string, b: number): void; function y(a: string, b: number): void { console.log(a, b); } y('a'); y('a', 1); ``` Function parameter comparison succeeds if the source and target parameters are assignable to supertypes or subtypes (bivariance). ```typescript // Supertype class X { a: string; constructor(value: string) { this.a = value; } } // Subtype class Y extends X {} // Subtype class Z extends X {} type GetA = (x: X) => string; const getA: GetA = x => x.a; // Bivariance does accept supertypes console.log(getA(new X('x'))); // Valid console.log(getA(new Y('Y'))); // Valid console.log(getA(new Z('z'))); // Valid ``` Enums are comparable and valid with numbers and vice versa, but comparing Enum values from different Enum types is invalid. ```typescript enum X { A, B, } enum Y { A, B, C, } const xa: number = X.A; // Valid const ya: Y = 0; // Valid X.A === Y.A; // Invalid ``` Instances of a class are subject to a compatibility check for their private and protected members: ```typescript class X { public a: string; constructor(value: string) { this.a = value; } } class Y { private a: string; constructor(value: string) { this.a = value; } } let x: X = new Y('y'); // Invalid ``` The comparison check does not take into consideration the different inheritance hierarchy, for instance: ```typescript class X { public a: string; constructor(value: string) { this.a = value; } } class Y extends X { public a: string; constructor(value: string) { super(value); this.a = value; } } class Z { public a: string; constructor(value: string) { this.a = value; } } let x: X = new X('x'); let y: Y = new Y('y'); let z: Z = new Z('z'); x === y; // Valid x === z; // Valid even if z is from a different inheritance hierarchy ``` Generics are compared using their structures based on the resulting type after applying the generic parameter, only the final result is compared as a non-generic type. ```typescript interface X { a: T; } let x: X = { a: 1 }; let y: X = { a: 'a' }; x === y; // Invalid as the type argument is used in the final structure ``` ```typescript interface X {} const x: X = 1; const y: X = 'a'; x === y; // Valid as the type argument is not used in the final structure ``` When generics do not have their type argument specified, all the unspecified arguments are treated as types with "any": ```typescript type X = (x: T) => T; type Y = (y: K) => K; let x: X = x => x; let y: Y = y => y; x = y; // Valid ``` Remember: ```typescript let a: number = 1; let b: number = 2; a = b; // Valid, everything is assignable to itself let c: any; c = 1; // Valid, all types are assignable to any let d: unknown; d = 1; // Valid, all types are assignable to unknown let e: unknown; let e1: unknown = e; // Valid, unknown is only assignable to itself and any let e2: any = e; // Valid let e3: number = e; // Invalid let f: never; f = 1; // Invalid, nothing is assignable to never let g: void; let g1: any; g = 1; // Invalid, void is not assignable to or from anything expect any g = g1; // Valid ``` Please note that when "strictNullChecks" is enabled, "null" and "undefined" are treated similarly to "void"; otherwise, they are similar to "never". ### Types as Sets In TypeScript, a type is a set of possible values. This set is also referred to as the domain of the type. Each value of a type can be viewed as an element in a set. A type establishes the constraints that every element in the set must satisfy to be considered a member of that set. The primary task of TypeScript is to check and verify whether one set is a subset of another. TypeScript supports various types of sets: | Set term | TypeScript | Notes | | ------------------ | ------------------------------- | ------------------------------------------------------------------------------------------------------------------ | | Empty set | never | "never" contains anything apart itself | | Single element set | undefined / null / literal type | | | Finite set | boolean / union | | | Infinite set | string / number / object | | | Universal set | any / unknown | Every element is a member of "any" and every set is a subset of it / "unknown" is a type-safe counterpart of "any" | Here few examples: | TypeScript | Set term | Example | | --------------------- | ---------------------- | ------------------------------------------------------------------------------- | | never | ∅ (empty set) | const x: never = 'x'; // Error: Type 'string' is not assignable to type 'never' | | | | | Literal type | Single element set | type X = 'X'; | | | | type Y = 7; | | | | | Value assignable to T | Value ∈ T (member of) | type XY = 'X' \| 'Y'; | | | | const x: XY = 'X'; | | | | | T1 assignable to T2 | T1 ⊆ T2 (subset of) | type XY = 'X' \| 'Y'; | | | | const x: XY = 'X'; | | | | const j: XY = 'J'; // Type '"J"' is not assignable to type 'XY'. | | | | | | T1 extends T2 | T1 ⊆ T2 (subset of) | type X = 'X' extends string ? true : false; | | | | | T1 \| T2 | T1 ∪ T2 (union) | type XY = 'X' \| 'Y'; | | | | type JK = 1 \| 2; | | | | | T1 & T2 | T1 ∩ T2 (intersection) | type X = \{ a: string \} | | | | type Y = \{ b: string \} | | | | type XY = X & Y | | | | const x: XY = \{ a: 'a', b: 'b' \} | | | | | unknown | Universal set | const x: unknown = 1 | An union, (T1 | T2) creates a wider set (both): ```typescript type X = { a: string; }; type Y = { b: string; }; type XY = X | Y; const r: XY = { a: 'a', b: 'x' }; // Valid ``` An intersection, (T1 & T2) create a narrower set (only shared): ```typescript type X = { a: string; }; type Y = { a: string; b: string; }; type XY = X & Y; const r: XY = { a: 'a' }; // Invalid const j: XY = { a: 'a', b: 'b' }; // Valid ``` The `extends` keyword could be considered as a "subset of" in this context. It sets a constraint for a type. The extends used with a generic, take the generic as an infinite set and it will constrain it to a more specific type. Please note that `extends` has nothing to do with hierarchy in a OOP sense (there is no this concept in TypeScript). TypeScript works with sets and does not have a strict hierarchy, infact, as in the example below, two types could overlap without either being a subtype of the other type (TypeScript considers the structure, shape of the objects). ```typescript interface X { a: string; } interface Y extends X { b: string; } interface Z extends Y { c: string; } const z: Z = { a: 'a', b: 'b', c: 'c' }; interface X1 { a: string; } interface Y1 { a: string; b: string; } interface Z1 { a: string; b: string; c: string; } const z1: Z1 = { a: 'a', b: 'b', c: 'c' }; const r: Z1 = z; // Valid ``` ### Assign a type: Type Declarations and Type Assertions A type can be assigned in different ways in TypeScript: #### Type Declaration In the following example, we use x: X (": Type") to declare a type for the variable x. ```typescript type X = { a: string; }; // Type declaration const x: X = { a: 'a', }; ``` If the variable is not in the specified format, TypeScript will report an error. For instance: ```typescript type X = { a: string; }; const x: X = { a: 'a', b: 'b', // Error: Object literal may only specify known properties }; ``` #### Type Assertion It is possible to add an assertion by using the `as` keyword. This tells the compiler that the developer has more information about a type and silences any errors that may occur. For example: ```typescript type X = { a: string; }; const x = { a: 'a', b: 'b', } as X; ``` In the above example, the object x is asserted to have the type X using the as keyword. This informs the TypeScript compiler that the object conforms to the specified type, even though it has an additional property b not present in the type definition. Type assertions are useful in situations where a more specific type needs to be specified, especially when working with the DOM. For instance: ```typescript const myInput = document.getElementById('my_input') as HTMLInputElement; ``` Here, the type assertion as HTMLInputElement is used to tell TypeScript that the result of getElementById should be treated as an HTMLInputElement. Type assertions can also be used to remap keys, as shown in the example below with template literals: ```typescript type J = { [Property in keyof Type as `prefix_${string & Property}`]: () => Type[Property]; }; type X = { a: string; b: number; }; type Y = J; ``` In this example, the type `J` uses a mapped type with a template literal to remap the keys of Type. It creates new properties with a "prefix_" added to each key, and their corresponding values are functions returning the original property values. It is worth noting that when using a type assertion, TypeScript will not execute excess property checking. Therefore, it is generally preferable to use a Type Declaration when the structure of the object is known in advance. #### Ambient Declarations Ambient declarations are files that describe types for JavaScript code, they have a file name format as `.d.ts.`. They are usually imported and used to annotate existing JavaScript libraries or to add types to existing JS files in your project. Many common libraries types can be found at: [https://github.com/DefinitelyTyped/DefinitelyTyped/](https://github.com/DefinitelyTyped/DefinitelyTyped/) and can be installed using: ```shell npm install --save-dev @types/library-name ``` For your defined Ambient Declarations, you can import using the "triple-slash" reference: ```typescript /// ``` You can use Ambient Declarations even within JavaScript files using `// @ts-check`. The `declare` keyword enables type definitions for existing JavaScript code without importing it, serving as a placeholder for types from another file or globally. ### Property Checking and Excess Property Checking TypeScript is based on a structural type system but excess property checking is a property of TypeScript which allows it to check whether an object has the exact properties specified in the type. Excess Property Checking is performed when assigning object literals to variables or when passing them as arguments to the function's excess property, for instance. ```typescript type X = { a: string; }; const y = { a: 'a', b: 'b' }; const x: X = y; // Valid because structural typing const w: X = { a: 'a', b: 'b' }; // Invalid because excess property checking ``` ### Weak Types A type is considered weak when it contains nothing but a set of all-optional properties: ```typescript type X = { a?: string; b?: string; }; ``` TypeScript considers an error to assign anything to a weak type when there is no overlap, for instance, the following throws an error: ```typescript type Options = { a?: string; b?: string; }; const fn = (options: Options) => undefined; fn({ c: 'c' }); // Invalid ``` Although not recommended, if needed, it is possible to bypass this check by using type assertion: ```typescript type Options = { a?: string; b?: string; }; const fn = (options: Options) => undefined; fn({ c: 'c' } as Options); // Valid ``` Or by adding `unknown` to the index signature to the weak type: ```typescript type Options = { [prop: string]: unknown; a?: string; b?: string; }; const fn = (options: Options) => undefined; fn({ c: 'c' }); // Valid ``` ### Strict Object Literal Checking (Freshness) Strict object literal checking, sometimes referred to as "freshness", is a feature in TypeScript that helps catch excess or misspelled properties that would otherwise go unnoticed in normal structural type checks. When creating an object literal, the TypeScript compiler considers it "fresh." If the object literal is assigned to a variable or passed as a parameter, TypeScript will throw an error if the object literal specifies properties that do not exist in the target type. However, "freshness" disappears when an object literal is widened or a type assertion is used. Here are some examples to illustrate: ```typescript type X = { a: string }; type Y = { a: string; b: string }; let x: X; x = { a: 'a', b: 'b' }; // Freshness check: Invalid assignment var y: Y; y = { a: 'a', bx: 'bx' }; // Freshness check: Invalid assignment const fn = (x: X) => console.log(x.a); fn(x); fn(y); // Widening: No errors, structurally type compatible fn({ a: 'a', bx: 'b' }); // Freshness check: Invalid argument let c: X = { a: 'a' }; let d: Y = { a: 'a', b: '' }; c = d; // Widening: No Freshness check ``` ### Type Inference TypeScript can infer types when no annotation is provided during: * Variable initialization. * Member initialization. * Setting defaults for parameters. * Function return type. For example: ```typescript let x = 'x'; // The type inferred is string ``` The TypeScript compiler analyzes the value or expression and determines its type based on the available information. ### More Advanced Inferences When multiple expressions are used in type inference, TypeScript looks for the "best common types." For instance: ```typescript let x = [1, 'x', 1, null]; // The type inferred is: (string | number | null)[] ``` If the compiler cannot find the best common types, it returns a union type. For example: ```typescript let x = [new RegExp('x'), new Date()]; // Type inferred is: (RegExp | Date)[] ``` TypeScript utilizes "contextual typing" based on the variable's location to infer types. In the following example, the compiler knows that `e` is of type `MouseEvent` because of the `click` event type defined in the lib.d.ts file, which contains ambient declarations for various common JavaScript constructs and the DOM: ```typescript window.addEventListener('click', function (e) {}); // The inferred type of e is MouseEvent ``` ### Type Widening Type widening is the process in which TypeScript assigns a type to a variable initialized when no type annotation was provided. It allows narrow to wider types but not vice versa. In the following example: ```typescript let x = 'x'; // TypeScript infers as string, a wide type let y: 'y' | 'x' = 'y'; // y types is a union of literal types y = x; // Invalid Type 'string' is not assignable to type '"x" | "y"'. ``` TypeScript assigns `string` to `x` based on the single value provided during initialization (`x`), this is an example of widening. TypeScript provides ways to have control of the widening process, for instance using "const". ### Const Using the `const` keyword when declaring a variable results in a narrower type inference in TypeScript. For example: ```typescript const x = 'x'; // TypeScript infers the type of x as 'x', a narrower type let y: 'y' | 'x' = 'y'; y = x; // Valid: The type of x is inferred as 'x' ``` By using `const` to declare the variable x, its type is narrowed to the specific literal value 'x'. Since the type of x is narrowed, it can be assigned to the variable y without any error. The reason the type can be inferred is because `const` variables cannot be reassigned, so their type can be narrowed down to a specific literal type, in this case, the literal type 'x'. #### Const Modifier on Type Parameters From version 5.0 of TypeScript, it is possible to specify the `const` attribute on a generic type parameter. This allows for inferring the most precise type possible. Let's see an example without using `const`: ```typescript function identity(value: T) { // No const here return value; } const values = identity({ a: 'a', b: 'b' }); // Type infered is: { a: string; b: string; } ``` As you can see, the properties `a` and `b` are inferred with a type of `string` . Now, let's see the difference with the `const` version: ```typescript function identity(value: T) { // Using const modifier on type parameters return value; } const values = identity({ a: 'a', b: 'b' }); // Type infered is: { a: "a"; b: "b"; } ``` Now we can see that the properties `a` and `b` are inferred as `const`, so `a` and `b` are treated as string literals rather than just `string` types. #### Const assertion This feature allows you to declare a variable with a more precise literal type based on its initialization value, signifying to the compiler that the value should be treated as an immutable literal. Here are a few examples: On a single property: ```typescript const v = { x: 3 as const, }; v.x = 3; ``` On an entire object: ```typescript const v = { x: 1, y: 2, } as const; ``` This can be particularly useful when defining the type for a tuple: ```typescript const x = [1, 2, 3]; // number[] const y = [1, 2, 3] as const; // Tuple of readonly [1, 2, 3] ``` ### Explicit Type Annotation We can be specific and pass a type, in the following example property `x` is of type `number`: ```typescript const v = { x: 1, // Inferred type: number (widening) }; v.x = 3; // Valid ``` We can make the type annotation more specific by using a union of literal types: ```typescript const v: { x: 1 | 2 | 3 } = { x: 1, // x is now a union of literal types: 1 | 2 | 3 }; v.x = 3; // Valid v.x = 100; // Invalid ``` ### Type Narrowing Type Narrowing is the process in TypeScript where a general type is narrowed down to a more specific type. This occurs when TypeScript analyzes the code and determines that certain conditions or operations can refine the type information. Narrowing types can occur in different ways, including: #### Conditions By using conditional statements, such as `if` or `switch`, TypeScript can narrow down the type based on the outcome of the condition. For example: ```typescript let x: number | undefined = 10; if (x !== undefined) { x += 100; // The type is number, which had been narrowed by the condition } ``` #### Throwing or returning Throwing an error or returning early from a branch can be used to help TypeScript narrow down a type. For example: ```typescript let x: number | undefined = 10; if (x === undefined) { throw 'error'; } x += 100; ``` Other ways to narrow down types in TypeScript include: * `instanceof` operator: Used to check if an object is an instance of a specific class. * `in` operator: Used to check if a property exists in an object. * `typeof` operator: Used to check the type of a value at runtime. * Built-in functions like `Array.isArray()`: Used to check if a value is an array. #### Discriminated Union Using a "Discriminated Union" is a pattern in TypeScript where an explicit "tag" is added to objects to distinguish between different types within a union. This pattern is also referred to as a "tagged union." In the following example, the "tag" is represented by the property "type": ```typescript type A = { type: 'type_a'; value: number }; type B = { type: 'type_b'; value: string }; const x = (input: A | B): string | number => { switch (input.type) { case 'type_a': return input.value + 100; // type is A case 'type_b': return input.value + 'extra'; // type is B } }; ``` #### User-Defined Type Guards In cases where TypeScript is unable to determine a type, it is possible to write a helper function known as a "user-defined type guard." In the following example, we will utilize a Type Predicate to narrow down the type after applying certain filtering: ```typescript const data = ['a', null, 'c', 'd', null, 'f']; const r1 = data.filter(x => x != null); // The type is (string | null)[], TypeScript was not able to infer the type properly const isValid = (item: string | null): item is string => item !== null; // Custom type guard const r2 = data.filter(isValid); // The type is fine now string[], by using the predicate type guard we were able to narrow the type ``` ## Primitive Types TypeScript supports 7 primitive types. A primitive data type refers to a type that is not an object and does not have any methods associated with it. In TypeScript, all primitive types are immutable, meaning their values cannot be changed once they are assigned. ### string The `string` primitive type stores textual data, and the value is always double or single-quoted. ```typescript const x: string = 'x'; const y: string = 'y'; ``` Strings can span multiple lines if surrounded by the backtick (`) character: ```typescript let sentence: string = `xxx, yyy`; ``` ### boolean The `boolean` data type in TypeScript stores a binary value, either `true` or `false`. ```typescript const isReady: boolean = true; ``` ### number A `number` data type in TypeScript is represented with a 64-bit floating point value. A `number` type can represent integers and fractions. TypeScript also supports hexadecimal, binary, and octal, for instance: ```typescript const decimal: number = 10; const hexadecimal: number = 0xa00d; // Hexadecimal starts with 0x const binary: number = 0b1010; // Binary starts with 0b const octal: number = 0o633; // Octal starts with 0o ``` ### bigInt A `bigInt` represents numeric values that are very large (253 – 1) and cannot be represented with a `number`. A `bigInt` can be created by calling the built-in function `BigInt()` or by adding `n` to the end of any integer numeric literal: ```typescript const x: bigint = BigInt(9007199254740991); const y: bigint = 9007199254740991n; ``` Notes: * `bigInt` values cannot be mixed with `number` and cannot be used with built-in `Math`, they must be coerced to the same type. * `bigInt` values are available only if target configuration is ES2020 or higher. ### Symbol Symbols are unique identifiers that can be used as property keys in objects to prevent naming conflicts. ```typescript type Obj = { [sym: symbol]: number; }; const a = Symbol('a'); const b = Symbol('b'); let obj: Obj = {}; obj[a] = 123; obj[b] = 456; console.log(obj[a]); // 123 console.log(obj[b]); // 456 ``` ### null and undefined `null` and `undefined` types both represent no value or the absence of any value. The `undefined` type means the value is not assigned or initialized or indicates an unintentional absence of value. The `null` type means that we know that the field does not have a value, so value is unavailable, it indicates an intentional absence of value. ### Array An `array` is a data type that can store multiple values of the same type or not. It can be defined using the following syntax: ```typescript const x: string[] = ['a', 'b']; const y: Array = ['a', 'b']; const j: Array = ['a', 1, 'b', 2]; // Union ``` TypeScript supports readonly arrays using the following syntax: ```typescript const x: readonly string[] = ['a', 'b']; // Readonly modifier const y: ReadonlyArray = ['a', 'b']; const j: ReadonlyArray = ['a', 1, 'b', 2]; j.push('x'); // Invalid ``` TypeScript supports tuple and readonly tuple: ```typescript const x: [string, number] = ['a', 1]; const y: readonly [string, number] = ['a', 1]; ``` ### any The `any` data type represents literally "any" value, it is the default value when TypeScript cannot infer the type or is not specified. When using `any` TypeScript compiler skips the type checking so there is no type safety when `any` is being used. Generally do not use `any` to silence the compiler when an error occurs, instead focus on fixing the error as with using `any` it is possible to break contracts and we lose the benefits of TypeScript autocomplete. The `any` type could be useful during a gradual migration from JavaScript to TypeScript, as it can silence the compiler. For new projects use TypeScript configuration `noImplicitAny` which enables TypeScript to issue errors where `any` is used or inferred. The `any`type is usually a source of errors which can mask real problems with your types. Avoid using it as much as possible. ## Type Annotations On variables declared using `var`, `let` and `const`, it is possible to optionally add a type: ```typescript const x: number = 1; ``` TypeScript does a good job of inferring types, especially when simple one, so these declarations in most cases are not necessary. On functions is possible to add type annotations to parameters: ```typescript function sum(a: number, b: number) { return a + b; } ``` The following is an example using a anonymous functions (so called lambda function): ```typescript const sum = (a: number, b: number) => a + b; ``` These annotation can be avoided when a default value for a parameter is present: ```typescript const sum = (a = 10, b: number) => a + b; ``` Return type annotations can be added to functions: ```typescript const sum = (a = 10, b: number): number => a + b; ``` This is useful especially for more complex functions as writing expliciting the return type before an implementation can help better think about the function. Generally consider annotating type signatures but not the body local variables and add types always to object literals. ## Optional Properties An object can specify Optional Properties by adding a question mark `?` to the end of the property name: ```typescript type X = { a: number; b?: number; // Optional }; ``` It is possible to specify a default value when a property is optional: ```typescript type X = { a: number; b?: number; }; const x = ({ a, b = 100 }: X) => a + b; ``` ## Readonly Properties Is it possible to prevent writing on a property by using the modifier `readonly`which makes sure that the property cannot be re-written but does not provide any guarantee of total immutability: ```typescript interface Y { readonly a: number; } type X = { readonly a: number; }; type J = Readonly<{ a: number; }>; type K = { readonly [index: number]: string; }; ``` ## Index Signatures In TypeScript we can use as index signature `string`, `number`, and `symbol`: ```typescript type K = { [name: string | number]: string; }; const k: K = { x: 'x', 1: 'b' }; console.log(k['x']); console.log(k[1]); console.log(k['1']); // Same result as k[1] ``` Please note that JavaScript automatically converts an index with `number` to an index with `string` so `k[1]` or `k["1"]` return the same value. ## Extending Types It is possible to extend an `interface` (copy members from another type): ```typescript interface X { a: string; } interface Y extends X { b: string; } ``` It is also possible to extend from multiple types: ```typescript interface A { a: string; } interface B { b: string; } interface Y extends A, B { y: string; } ``` The `extends` keyword works only on interfaces and classes, for types use an intersection: ```typescript type A = { a: number; }; type B = { b: number; }; type C = A & B; ``` It is possible to extend a type using an inference but not vice versa: ```typescript type A = { a: string; }; interface B extends A { b: string; } ``` ## Literal Types A Literal Type is a single element set from a collective type, it defines a very exact value that is a JavaScript primitive. Literal Types in TypeScript are numbers, strings, and booleans. Example of literals: ```typescript const a = 'a'; // String literal type const b = 1; // Numeric literal type const c = true; // Boolean literal type ``` String, Numeric, and Boolean Literal Types are used in unions, type guards, and type aliases. In the following example, you can see a union type alias. `O` consists of only the specified values, no other string is valid: ```typescript type O = 'a' | 'b' | 'c'; ``` ## Literal Inference Literal Inference is a feature in TypeScript that allows the type of a variable or parameter to be inferred based on its value. In the following example we can see that TypeScript considers `x` a literal type as the value cannot be changed any time later, when instead `y` is inferred as string as it can be modified any time later. ```typescript const x = 'x'; // Literal type of 'x', because this value cannot be changed let y = 'y'; // Type string, as we can change this value ``` In the following example we can see that `o.x` was inferred as a `string` (and not a literal of `a`) as TypeScript considers that the value can be changed any time later. ```typescript type X = 'a' | 'b'; let o = { x: 'a', // This is a wider string }; const fn = (x: X) => `${x}-foo`; console.log(fn(o.x)); // Argument of type 'string' is not assignable to parameter of type 'X' ``` As you can see the code throws an error when passing `o.x` to `fn` as X is a narrower type. We can solve this issue by using type assertion using `const` or the `X` type: ```typescript let o = { x: 'a' as const, }; ``` or: ```typescript let o = { x: 'a' as X, }; ``` ## strictNullChecks `strictNullChecks` is a TypeScript compiler option that enforces strict null checking. When this option is enabled, variables and parameters can only be assigned `null` or `undefined` if they have been explicitly declared to be of that type using the union type `null` | `undefined`. If a variable or parameter is not explicitly declared as nullable, TypeScript will generate an error to prevent potential runtime errors. ## Enums In TypeScript, an `enum` is a set of named constant values. ```typescript enum Color { Red = '#ff0000', Green = '#00ff00', Blue = '#0000ff', } ``` Enums can be defined in different ways: ### Numeric enums In TypeScript, a Numeric Enum is an Enum where each constant is assigned a numeric value, starting from 0 by default. ```typescript enum Size { Small, // value starts from 0 Medium, Large, } ``` It is possible to specify custom values by explicitly assigning them: ```typescript enum Size { Small = 10, Medium, Large, } console.log(Size.Medium); // 11 ``` ### String enums In TypeScript, a String enum is an Enum where each constant is assigned a string value. ```typescript enum Language { English = 'EN', Spanish = 'ES', } ``` Note: TypeScript allows the usage of heterogeneous Enums where string and numeric members can coexist. ### Constant enums A constant enum in TypeScript is a special type of Enum where all the values are known at compile time and are inlined wherever the enum is used, resulting in more efficient code. ```typescript const enum Language { English = 'EN', Spanish = 'ES', } console.log(Language.English); ``` Will be compiled into: ```typescript console.log('EN' /* Language.English */); ``` Notes: Const Enums have hardcoded values, erasing the Enum, which can be more efficient in self-contained libraries but is generally not desirable. Also, Const enums cannot have computed members. ### Reverse mapping In TypeScript, reverse mappings in Enums refer to the ability to retrieve the Enum member name from its value. By default, Enum members have forward mappings from name to value, but reverse mappings can be created by explicitly setting values for each member. Reverse mappings are useful when you need to look up an Enum member by its value, or when you need to iterate over all the Enum members. Note that only numeric enums members will generate reverse mappings, while String Enum members do not get a reverse mapping generated at all. The following enum: ```typescript enum Grade { A = 90, B = 80, C = 70, F = 'fail', } ``` Compiles to: ```javascript 'use strict'; var Grade; (function (Grade) { Grade[(Grade['A'] = 90)] = 'A'; Grade[(Grade['B'] = 80)] = 'B'; Grade[(Grade['C'] = 70)] = 'C'; Grade['F'] = 'fail'; })(Grade || (Grade = {})); ``` Therefore, mapping values to keys works for numeric enum members, but not for string enum members: ```typescript enum Grade { A = 90, B = 80, C = 70, F = 'fail', } const myGrade = Grade.A; console.log(Grade[myGrade]); // A console.log(Grade[90]); // A const failGrade = Grade.F; console.log(failGrade); // fail console.log(Grade[failGrade]); // Element implicitly has an 'any' type because index expression is not of type 'number'. ``` ### Ambient enums An ambient enum in TypeScript is a type of Enum that is defined in a declaration file (*.d.ts) without an associated implementation. It allows you to define a set of named constants that can be used in a type-safe way across different files without having to import the implementation details in each file. ### Computed and constant members In TypeScript, a computed member is a member of an Enum that has a value calculated at runtime, while a constant member is a member whose value is set at compile-time and cannot be changed during runtime. Computed members are allowed in regular Enums, while constant members are allowed in both regular and const enums. ```typescript // Constant members enum Color { Red = 1, Green = 5, Blue = Red + Green, } console.log(Color.Blue); // 6 generation at compilation time ``` ```typescript // Computed members enum Color { Red = 1, Green = Math.pow(2, 2), Blue = Math.floor(Math.random() * 3) + 1, } console.log(Color.Blue); // random number generated at run time ``` Enums are denoted by unions comprising their member types. The values of each member can be determined through constant or non-constant expressions, with members possessing constant values being assigned literal types. To illustrate, consider the declaration of type E and its subtypes E.A, E.B, and E.C. In this case, E represents the union E.A | E.B | E.C. ```typescript const identity = (value: number) => value; enum E { A = 2 * 5, // Numeric literal B = 'bar', // String literal C = identity(42), // Opaque computed } console.log(E.C); //42 ``` ## Narrowing TypeScript narrowing is the process of refining the type of a variable within a conditional block. This is useful when working with union types, where a variable can have more than one type. TypeScript recognizes several ways to narrow the type: ### typeof type guards The typeof type guard is one specific type guard in TypeScript that checks the type of a variable based on its built-in JavaScript type. ```typescript const fn = (x: number | string) => { if (typeof x === 'number') { return x + 1; // x is number } return -1; }; ``` ### Truthiness narrowing Truthiness narrowing in TypeScript works by checking whether a variable is truthy or falsy to narrow its type accordingly. ```typescript const toUpperCase = (name: string | null) => { if (name) { return name.toUpperCase(); } else { return null; } }; ``` ### Equality narrowing Equality narrowing in TypeScript works by checking whether a variable is equal to a specific value or not, to narrow its type accordingly. It is used in conjunction with `switch` statements and equality operators such as `===`, `!==`, `==`, and `!=` to narrow down types. ```typescript const checkStatus = (status: 'success' | 'error') => { switch (status) { case 'success': return true; case 'error': return null; } }; ``` ### In Operator narrowing The `in` Operator narrowing in TypeScript is a way to narrow the type of a variable based on whether a property exists within the variable's type. ```typescript type Dog = { name: string; breed: string; }; type Cat = { name: string; likesCream: boolean; }; const getAnimalType = (pet: Dog | Cat) => { if ('breed' in pet) { return 'dog'; } else { return 'cat'; } }; ``` ### instanceof narrowing The `instanceof` operator narrowing in TypeScript is a way to narrow the type of a variable based on its constructor function, by checking if an object is an instance of a certain class or interface. ```typescript class Square { constructor(public width: number) {} } class Rectangle { constructor( public width: number, public height: number ) {} } function area(shape: Square | Rectangle) { if (shape instanceof Square) { return shape.width * shape.width; } else { return shape.width * shape.height; } } const square = new Square(5); const rectangle = new Rectangle(5, 10); console.log(area(square)); // 25 console.log(area(rectangle)); // 50 ``` ## Assignments TypeScript narrowing using assignments is a way to narrow the type of a variable based on the value assigned to it. When a variable is assigned a value, TypeScript infers its type based on the assigned value, and it narrows the type of the variable to match the inferred type. ```typescript let value: string | number; value = 'hello'; if (typeof value === 'string') { console.log(value.toUpperCase()); } value = 42; if (typeof value === 'number') { console.log(value.toFixed(2)); } ``` ## Control Flow Analysis Control Flow Analysis in TypeScript is a way to statically analyze the code flow to infer the types of variables, allowing the compiler to narrow the types of those variables as needed, based on the results of the analysis. Prior to TypeScript 4.4, code flow analysis would only be applied to code within an if statement, but from TypeScript 4.4, it can also be applied to conditional expressions and discriminant property accesses indirectly referenced through const variables. For example: ```typescript const f1 = (x: unknown) => { const isString = typeof x === 'string'; if (isString) { x.length; } }; const f2 = ( obj: { kind: 'foo'; foo: string } | { kind: 'bar'; bar: number } ) => { const isFoo = obj.kind === 'foo'; if (isFoo) { obj.foo; } else { obj.bar; } }; ``` Some examples where narrowing does not occur: ```typescript const f1 = (x: unknown) => { let isString = typeof x === 'string'; if (isString) { x.length; // Error, no narrowing because isString it is not const } }; const f6 = ( obj: { kind: 'foo'; foo: string } | { kind: 'bar'; bar: number } ) => { const isFoo = obj.kind === 'foo'; obj = obj; if (isFoo) { obj.foo; // Error, no narrowing because obj is assigned in function body } }; ``` Notes: Up to five levels of indirection are analyzed in conditional expressions. ## Type Predicates Type Predicates in TypeScript are functions that return a boolean value and are used to narrow the type of a variable to a more specific type. ```typescript const isString = (value: unknown): value is string => typeof value === 'string'; const foo = (bar: unknown) => { if (isString(bar)) { console.log(bar.toUpperCase()); } else { console.log('not a string'); } }; ``` ## Discriminated Unions Discriminated Unions in TypeScript are a type of union type that uses a common property, known as the discriminant, to narrow down the set of possible types for the union. ```typescript type Square = { kind: 'square'; // Discriminant size: number; }; type Circle = { kind: 'circle'; // Discriminant radius: number; }; type Shape = Square | Circle; const area = (shape: Shape) => { switch (shape.kind) { case 'square': return Math.pow(shape.size, 2); case 'circle': return Math.PI * Math.pow(shape.radius, 2); } }; const square: Square = { kind: 'square', size: 5 }; const circle: Circle = { kind: 'circle', radius: 2 }; console.log(area(square)); // 25 console.log(area(circle)); // 12.566370614359172 ``` ## The never Type When a variable is narrowed to a type that cannot contain any values, the TypeScript compiler will infer that the variable must be of the `never` type. This is because The never Type represents a value that can never be produced. ```typescript const printValue = (val: string | number) => { if (typeof val === 'string') { console.log(val.toUpperCase()); } else if (typeof val === 'number') { console.log(val.toFixed(2)); } else { // val has type never here because it can never be anything other than a string or a number const neverVal: never = val; console.log(`Unexpected value: ${neverVal}`); } }; ``` ## Exhaustiveness checking Exhaustiveness checking is a feature in TypeScript that ensures all possible cases of a discriminated union are handled in a `switch` statement or an `if` statement. ```typescript type Direction = 'up' | 'down'; const move = (direction: Direction) => { switch (direction) { case 'up': console.log('Moving up'); break; case 'down': console.log('Moving down'); break; default: const exhaustiveCheck: never = direction; console.log(exhaustiveCheck); // This line will never be executed } }; ``` The `never` type is used to ensure that the default case is exhaustive and that TypeScript will raise an error if a new value is added to the Direction type without being handled in the switch statement. ## Object Types In TypeScript, object types describe the shape of an object. They specify the names and types of the object's properties, as well as whether those properties are required or optional. In TypeScript, you can define object types in two primary ways: Interface which defines the shape of an object by specifying the names, types, and optionality of its properties. ```typescript interface User { name: string; age: number; email?: string; } ``` Type alias, similar to an interface, defines the shape of an object. However, it can also create a new custom type that is based on an existing type or a combination of existing types. This includes defining union types, intersection types, and other complex types. ```typescript type Point = { x: number; y: number; }; ``` It also possible to define a type anonymously: ```typescript const sum = (x: { a: number; b: number }) => x.a + x.b; console.log(sum({ a: 5, b: 1 })); ``` ## Tuple Type (Anonymous) A Tuple Type is a type that represents an array with a fixed number of elements and their corresponding types. A tuple type enforces a specific number of elements and their respective types in a fixed order. Tuple types are useful when you want to represent a collection of values with specific types, where the position of each element in the array has a specific meaning. ```typescript type Point = [number, number]; ``` ## Named Tuple Type (Labeled) Tuple types can include optional labels or names for each element. These labels are for readability and tooling assistance, and do not affect the operations you can perform with them. ```typescript type T = string; type Tuple1 = [T, T]; type Tuple2 = [a: T, b: T]; type Tuple3 = [a: T, T]; // Named Tuple plus Anonymous Tuple ``` ## Fixed Length Tuple A Fixed Length Tuple is a specific type of tuple that enforces a fixed number of elements of specific types, and disallows any modifications to the length of the tuple once it is defined. Fixed Length Tuples are useful when you need to represent a collection of values with a specific number of elements and specific types, and you want to ensure that the length and types of the tuple cannot be changed inadvertently. ```typescript const x = [10, 'hello'] as const; x.push(2); // Error ``` ## Union Type A Union Type is a type that represents a value that can be one of several types. Union Types are denoted using the `|` symbol between each possible type. ```typescript let x: string | number; x = 'hello'; // Valid x = 123; // Valid ``` ## Intersection Types An Intersection Type is a type that represents a value that has all the properties of two or more types. Intersection Types are denoted using the `&` symbol between each type. ```typescript type X = { a: string; }; type Y = { b: string; }; type J = X & Y; // Intersection const j: J = { a: 'a', b: 'b', }; ``` ## Type Indexing Type indexing refers to the ability to define types that can be indexed by a key that is not known in advance, using an index signature to specify the type for properties that are not explicitly declared. ```typescript type Dictionary = { [key: string]: T; }; const myDict: Dictionary = { a: 'a', b: 'b' }; console.log(myDict['a']); // Returns a ``` ## Type from Value Type from Value in TypeScript refers to the automatic inference of a type from a value or expression through type inference. ```typescript const x = 'x'; // TypeScript infers 'x' as a string literal with 'const' (immutable), but widens it to 'string' with 'let' (reassignable). ``` ## Type from Func Return Type from Func Return refers to the ability to automatically infer the return type of a function based on its implementation. This allows TypeScript to determine the type of the value returned by the function without explicit type annotations. ```typescript const add = (x: number, y: number) => x + y; // TypeScript can infer that the return type of the function is a number ``` ## Type from Module Type from Module refers to the ability to use a module's exported values to automatically infer their types. When a module exports a value with a specific type, TypeScript can use that information to automatically infer the type of that value when it is imported into another module. ```typescript // calc.ts export const add = (x: number, y: number) => x + y; // index.ts import { add } from 'calc'; const r = add(1, 2); // r is number ``` ## Mapped Types Mapped Types in TypeScript allow you to create new types based on an existing type by transforming each property using a mapping function. By mapping existing types, you can create new types that represent the same information in a different format. To create a mapped type, you access the properties of an existing type using the `keyof` operator and then alter them to produce a new type. In the following example: ```typescript type MyMappedType = { [P in keyof T]: T[P][]; }; type MyType = { foo: string; bar: number; }; type MyNewType = MyMappedType; const x: MyNewType = { foo: ['hello', 'world'], bar: [1, 2, 3], }; ``` we define MyMappedType to map over T's properties, creating a new type with each property as an array of its original type. Using this, we create MyNewType to represent the same info as MyType, but with each property as an array. ## Mapped Type Modifiers Mapped Type Modifiers in TypeScript enable the transformation of properties within an existing type: * `readonly` or `+readonly`: This renders a property in the mapped type as read-only. * `-readonly`: This allows a property in the mapped type to be mutable. * `?`: This designates a property in the mapped type as optional. Examples: ```typescript type ReadOnly = { readonly [P in keyof T]: T[P] }; // All properties marked as read-only type Mutable = { -readonly [P in keyof T]: T[P] }; // All properties marked as mutable type MyPartial = { [P in keyof T]?: T[P] }; // All properties marked as optional ``` ## Conditional Types Conditional Types are a way to create a type that depends on a condition, where the type to be created is determined based on the result of the condition. They are defined using the `extends` keyword and a ternary operator to conditionally choose between two types. ```typescript type IsArray = T extends any[] ? true : false; const myArray = [1, 2, 3]; const myNumber = 42; type IsMyArrayAnArray = IsArray; // Type true type IsMyNumberAnArray = IsArray; // Type false ``` ## Distributive Conditional Types Distributive Conditional Types are a feature that allow a type to be distributed over a union of types, by applying a transformation to each member of the union individually. This can be especially useful when working with mapped types or higher-order types. ```typescript type Nullable = T extends any ? T | null : never; type NumberOrBool = number | boolean; type NullableNumberOrBool = Nullable; // number | boolean | null ``` ## infer Type Inference in Conditional Types The `infer`keyword is used in conditional types to infer (extract) the type of a generic parameter from a type that depends on it. This allows you to write more flexible and reusable type definitions. ```typescript type ElementType = T extends (infer U)[] ? U : never; type Numbers = ElementType; // number type Strings = ElementType; // string ``` ## Predefined Conditional Types In TypeScript, Predefined Conditional Types are built-in conditional types provided by the language. They are designed to perform common type transformations based on the characteristics of a given type. `Exclude`: This type removes all the types from Type that are assignable to ExcludedType. `Extract`: This type extracts all the types from Union that are assignable to Type. `NonNullable`: This type removes null and undefined from Type. `ReturnType`: This type extracts the return type of a function Type. `Parameters`: This type extracts the parameter types of a function Type. `Required`: This type makes all properties in Type required. `Partial`: This type makes all properties in Type optional. `Readonly`: This type makes all properties in Type readonly. ## Template Union Types Template union types can be used to merge and manipulate text inside the type system for instance: ```typescript type Status = 'active' | 'inactive'; type Products = 'p1' | 'p2'; type ProductId = `id-${Products}-${Status}`; // "id-p1-active" | "id-p1-inactive" | "id-p2-active" | "id-p2-inactive" ``` ## Any type The `any` type is a special type (universal supertype) that can be used to represent any type of value (primitives, objects, arrays, functions, errors, symbols). It is often used in situations where the type of a value is not known at compile time, or when working with values from external APIs or libraries that do not have TypeScript typings. By utilizing `any` type, you are indicating to the TypeScript compiler that values should be represented without any limitations. To maximize type safety in your code, consider the following: * Limit the usage of `any` to specific cases where the type is truly unknown. * Do not return `any` types from a function, as this weakens type safety in code that uses it. * Instead of `any` use `@ts-ignore` if you need to silence the compiler. ```typescript let value: any; value = true; // Valid value = 7; // Valid ``` ## Unknown type In TypeScript, the `unknown` type represents a value that is of an unknown type. Unlike `any` type, which allows for any type of value, `unknown` requires a type check or assertion before it can be used in a specific way so no operations are permitted on an `unknown` without first asserting or narrowing to a more specific type. The `unknown` type is only assignable to any type and the `unknown` type itself, it is a type-safe alternative to `any`. ```typescript let value: unknown; let value1: unknown = value; // Valid let value2: any = value; // Valid let value3: boolean = value; // Invalid let value4: number = value; // Invalid ``` ```typescript const add = (a: unknown, b: unknown): number | undefined => typeof a === 'number' && typeof b === 'number' ? a + b : undefined; console.log(add(1, 2)); // 3 console.log(add('x', 2)); // undefined ``` ## Void type The `void` type is used to indicate that a function does not return a value. ```typescript const sayHello = (): void => { console.log('Hello!'); }; ``` ## Never type The `never` type represents values that never occur. It is used to denote functions or expressions that never return or throw an error. For instance an infinite loop: ```typescript const infiniteLoop = (): never => { while (true) { // do something } }; ``` Throwing an error: ```typescript const throwError = (message: string): never => { throw new Error(message); }; ``` The `never` type is useful in ensuring type safety and catching potential errors in your code. It helps TypeScript analyze and infer more precise types when used in combination with other types and control flow statements, for instance: ```typescript type Direction = 'up' | 'down'; const move = (direction: Direction): void => { switch (direction) { case 'up': // move up break; case 'down': // move down break; default: const exhaustiveCheck: never = direction; throw new Error(`Unhandled direction: ${exhaustiveCheck}`); } }; ``` ## Interface and Type ### Common Syntax In TypeScript, interfaces define the structure of objects, specifying the names and types of properties or methods that an object must have. The common syntax for defining an interface in TypeScript is as follows: ```typescript interface InterfaceName { property1: Type1; // ... method1(arg1: ArgType1, arg2: ArgType2): ReturnType; // ... } ``` Similarly for type definition: ```typescript type TypeName = { property1: Type1; // ... method1(arg1: ArgType1, arg2: ArgType2): ReturnType; // ... }; ``` `interface InterfaceName` or `type TypeName`: Defines the name of the interface. `property1`: `Type1`: Specifies the properties of the interface along with their corresponding types. Multiple properties can be defined, each separated by a semicolon. `method1(arg1: ArgType1, arg2: ArgType2): ReturnType;`: Specifies the methods of the interface. Methods are defined with their names, followed by a parameter list in parentheses and the return type. Multiple methods can be defined, each separated by a semicolon. Example interface: ```typescript interface Person { name: string; age: number; greet(): void; } ``` Example of type: ```typescript type TypeName = { property1: string; method1(arg1: string, arg2: string): string; }; ``` In TypeScript, types are used to define the shape of data and enforce type checking. There are several common syntaxes for defining types in TypeScript, depending on the specific use case. Here are some examples: ### Basic Types ```typescript let myNumber: number = 123; // number type let myBoolean: boolean = true; // boolean type let myArray: string[] = ['a', 'b']; // array of strings let myTuple: [string, number] = ['a', 123]; // tuple ``` ### Objects and Interfaces ```typescript const x: { name: string; age: number } = { name: 'Simon', age: 7 }; ``` ### Union and Intersection Types ```typescript type MyType = string | number; // Union type let myUnion: MyType = 'hello'; // Can be a string myUnion = 123; // Or a number type TypeA = { name: string }; type TypeB = { age: number }; type CombinedType = TypeA & TypeB; // Intersection type let myCombined: CombinedType = { name: 'John', age: 25 }; // Object with both name and age properties ``` ## Built-in Type Primitives TypeScript has several built-in type primitives that can be used to define variables, function parameters, and return types: * `number`: Represents numeric values, including integers and floating-point numbers. * `string`: Represents textual data * `boolean`: Represents logical values, which can be either true or false. * `null`: Represents the absence of a value. * `undefined`: Represents a value that has not been assigned or has not been defined. * `symbol`: Represents a unique identifier. Symbols are typically used as keys for object properties. * `bigint`: Represents arbitrary-precision integers. * `any`: Represents a dynamic or unknown type. Variables of type any can hold values of any type, and they bypass type checking. * `void`: Represents the absence of any type. It is commonly used as the return type of functions that do not return a value. * `never`: Represents a type for values that never occur. It is typically used as the return type of functions that throw an error or enter an infinite loop. ## Common Built-in JS Objects TypeScript is a superset of JavaScript, it includes all the commonly used built-in JavaScript objects. You can find an extensive list of these objects on the Mozilla Developer Network (MDN) documentation website: [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects) Here is a list of some commonly used built-in JavaScript objects: * Function * Object * Boolean * Error * Number * BigInt * Math * Date * String * RegExp * Array * Map * Set * Promise * Intl ## Overloads Function overloads in TypeScript allow you to define multiple function signatures for a single function name, enabling you to define functions that can be called in multiple ways. Here's an example: ```typescript // Overloads function sayHi(name: string): string; function sayHi(names: string[]): string[]; // Implementation function sayHi(name: unknown): unknown { if (typeof name === 'string') { return `Hi, ${name}!`; } else if (Array.isArray(name)) { return name.map(name => `Hi, ${name}!`); } throw new Error('Invalid value'); } sayHi('xx'); // Valid sayHi(['aa', 'bb']); // Valid ``` Here's another example of using function overloads within a `class`: ```typescript class Greeter { message: string; constructor(message: string) { this.message = message; } // overload sayHi(name: string): string; sayHi(names: string[]): ReadonlyArray; // implementation sayHi(name: unknown): unknown { if (typeof name === 'string') { return `${this.message}, ${name}!`; } else if (Array.isArray(name)) { return name.map(name => `${this.message}, ${name}!`); } throw new Error('value is invalid'); } } console.log(new Greeter('Hello').sayHi('Simon')); ``` ## Merging and Extension Merging and extension refer to two different concepts related to working with types and interfaces. Merging allows you to combine multiple declarations of the same name into a single definition, for example, when you define an interface with the same name multiple times: ```typescript interface X { a: string; } interface X { b: number; } const person: X = { a: 'a', b: 7, }; ``` Extension refers to the ability to extend or inherit from existing types or interfaces to create new ones. It is a mechanism to add additional properties or methods to an existing type without modifying its original definition. Example: ```typescript interface Animal { name: string; eat(): void; } interface Bird extends Animal { sing(): void; } const dog: Bird = { name: 'Bird 1', eat() { console.log('Eating'); }, sing() { console.log('Singing'); }, }; ``` ## Differences between Type and Interface Declaration merging (augmentation): Interfaces support declaration merging, which means that you can define multiple interfaces with the same name, and TypeScript will merge them into a single interface with the combined properties and methods. On the other hand, types do not support declaration merging. This can be helpful when you want to add extra functionality or customize existing types without modifying the original definitions or patching missing or incorrect types. ```typescript interface A { x: string; } interface A { y: string; } const j: A = { x: 'xx', y: 'yy', }; ``` Extending other types/interfaces: Both types and interfaces can extend other types/interfaces, but the syntax is different. With interfaces, you use the `extends` keyword to inherit properties and methods from other interfaces. However, an interface cannot extend a complex type like a union type. ```typescript interface A { x: string; y: number; } interface B extends A { z: string; } const car: B = { x: 'x', y: 123, z: 'z', }; ``` For types, you use the & operator to combine multiple types into a single type (intersection). ```typescript interface A { x: string; y: number; } type B = A & { j: string; }; const c: B = { x: 'x', y: 123, j: 'j', }; ``` Union and Intersection Types: Types are more flexible when it comes to defining Union and Intersection Types. With the `type` keyword, you can easily create union types using the `|` operator and intersection types using the `&` operator. While interfaces can also represent union types indirectly, they don't have built-in support for intersection types. ```typescript type Department = 'dep-x' | 'dep-y'; // Union type Person = { name: string; age: number; }; type Employee = { id: number; department: Department; }; type EmployeeInfo = Person & Employee; // Intersection ``` Example with interfaces: ```typescript interface A { x: 'x'; } interface B { y: 'y'; } type C = A | B; // Union of interfaces ``` ## Class ### Class Common Syntax The `class` keyword is used in TypeScript to define a class. Below, you can see an example: ```typescript class Person { private name: string; private age: number; constructor(name: string, age: number) { this.name = name; this.age = age; } public sayHi(): void { console.log( `Hello, my name is ${this.name} and I am ${this.age} years old.` ); } } ``` The `class` keyword is used to define a class named "Person". The class has two private properties: name of type `string` and age of type `number`. The constructor is defined using the `constructor` keyword. It takes name and age as parameters and assigns them to the corresponding properties. The class has a `public` method named sayHi that logs a greeting message. To create an instance of a class in TypeScript, you can use the `new` keyword followed by the class name, followed by parentheses `()`. For instance: ```typescript const myObject = new Person('John Doe', 25); myObject.sayHi(); // Output: Hello, my name is John Doe and I am 25 years old. ``` ### Constructor Constructors are special methods within a class that are used to initialize the object's properties when an instance of the class is created. ```typescript class Person { public name: string; public age: number; constructor(name: string, age: number) { this.name = name; this.age = age; } sayHello() { console.log( `Hello, my name is ${this.name} and I'm ${this.age} years old.` ); } } const john = new Person('Simon', 17); john.sayHello(); ``` It is possible to overload a constructor using the following syntax: ```typescript type Sex = 'm' | 'f'; class Person { name: string; age: number; sex: Sex; constructor(name: string, age: number, sex?: Sex); constructor(name: string, age: number, sex: Sex) { this.name = name; this.age = age; this.sex = sex ?? 'm'; } } const p1 = new Person('Simon', 17); const p2 = new Person('Alice', 22, 'f'); ``` In TypeScript, it is possible to define multiple constructor overloads, but you can have only one implementation that must be compatible with all the overloads, this can be achieved by using an optional parameter. ```typescript class Person { name: string; age: number; constructor(); constructor(name: string); constructor(name: string, age: number); constructor(name?: string, age?: number) { this.name = name ?? 'Unknown'; this.age = age ?? 0; } displayInfo() { console.log(`Name: ${this.name}, Age: ${this.age}`); } } const person1 = new Person(); person1.displayInfo(); // Name: Unknown, Age: 0 const person2 = new Person('John'); person2.displayInfo(); // Name: John, Age: 0 const person3 = new Person('Jane', 25); person3.displayInfo(); // Name: Jane, Age: 25 ``` ### Private and Protected Constructors In TypeScript, constructors can be marked as private or protected, which restricts their accessibility and usage. Private Constructors: Can be called only within the class itself. Private constructors are often used in scenarios where you want to enforce a singleton pattern or restrict the creation of instances to a factory method within the class Protected Constructors: Protected constructors are useful when you want to create a base class that should not be instantiated directly but can be extended by subclasses. ```typescript class BaseClass { protected constructor() {} } class DerivedClass extends BaseClass { private value: number; constructor(value: number) { super(); this.value = value; } } // Attempting to instantiate the base class directly will result in an error // const baseObj = new BaseClass(); // Error: Constructor of class 'BaseClass' is protected. // Create an instance of the derived class const derivedObj = new DerivedClass(10); ``` ### Access Modifiers Access Modifiers `private`, `protected`, and `public` are used to control the visibility and accessibility of class members, such as properties and methods, in TypeScript classes. These modifiers are essential for enforcing encapsulation and establishing boundaries for accessing and modifying the internal state of a class. The `private` modifier restricts access to the class member only within the containing class. The `protected` modifier allows access to the class member within the containing class and its derived classes. The `public` modifier provides unrestricted access to the class member, allowing it to be accessed from anywhere." ### Get and Set Getters and setters are special methods that allow you to define custom access and modification behavior for class properties. They enable you to encapsulate the internal state of an object and provide additional logic when getting or setting the values of properties. In TypeScript, getters and setters are defined using the `get` and `set` keywords respectively. Here's an example: ```typescript class MyClass { private _myProperty: string; constructor(value: string) { this._myProperty = value; } get myProperty(): string { return this._myProperty; } set myProperty(value: string) { this._myProperty = value; } } ``` ### Auto-Accessors in Classes TypeScript version 4.9 adds support for auto-accessors, a forthcoming ECMAScript feature. They resemble class properties but are declared with the "accessor" keyword. ```typescript class Animal { accessor name: string; constructor(name: string) { this.name = name; } } ``` Auto-accessors are "de-sugared" into private `get` and `set` accessors, operating on an inaccessible property. ```typescript class Animal { #__name: string; get name() { return this.#__name; } set name(value: string) { this.#__name = name; } constructor(name: string) { this.name = name; } } ``` ### this In TypeScript, the `this` keyword refers to the current instance of a class within its methods or constructors. It allows you to access and modify the properties and methods of the class from within its own scope. It provides a way to access and manipulate the internal state of an object within its own methods. ```typescript class Person { private name: string; constructor(name: string) { this.name = name; } public introduce(): void { console.log(`Hello, my name is ${this.name}.`); } } const person1 = new Person('Alice'); person1.introduce(); // Hello, my name is Alice. ``` ### Parameter Properties Parameter properties allow you to declare and initialize class properties directly within the constructor parameters avoiding boilerplate code, example: ```typescript class Person { constructor( private name: string, public age: number ) { // The "private" and "public" keywords in the constructor // automatically declare and initialize the corresponding class properties. } public introduce(): void { console.log( `Hello, my name is ${this.name} and I am ${this.age} years old.` ); } } const person = new Person('Alice', 25); person.introduce(); ``` ### Abstract Classes Abstract Classes are used in TypeScript mainly for inheritance, they provide a way to define common properties and methods that can be inherited by subclasses. This is useful when you want to define common behavior and enforce that subclasses implement certain methods. They provide a way to create a hierarchy of classes where the abstract base class provides a shared interface and common functionality for the subclasses. ```typescript abstract class Animal { protected name: string; constructor(name: string) { this.name = name; } abstract makeSound(): void; } class Cat extends Animal { makeSound(): void { console.log(`${this.name} meows.`); } } const cat = new Cat('Whiskers'); cat.makeSound(); // Output: Whiskers meows. ``` ### With Generics Classes with generics allow you to define reusable classes which can work with different types. ```typescript class Container { private item: T; constructor(item: T) { this.item = item; } getItem(): T { return this.item; } setItem(item: T): void { this.item = item; } } const container1 = new Container(42); console.log(container1.getItem()); // 42 const container2 = new Container('Hello'); container2.setItem('World'); console.log(container2.getItem()); // World ``` ### Decorators Decorators provide a mechanism to add metadata, modify behavior, validate, or extend the functionality of the target element. They are functions that execute at runtime. Multiple decorators can be applied to a declaration. Decorators are experimental features, and the following examples are only compatible with TypeScript version 5 or above using ES6. For TypeScript versions prior to 5, they should be enabled using the `experimentalDecorators` property in your `tsconfig.json` or by using `--experimentalDecorators` in your command line (but the following example won't work). Some of the common use cases for decorators include: * Watching property changes. * Watching method calls. * Adding extra properties or methods. * Runtime validation. * Automatic serialization and deserialization. * Logging. * Authorization and authentication. * Error guarding. Note: Decorators for version 5 do not allow decorating parameters. Types of decorators: #### Class Decorators Class Decorators are useful for extending an existing class, such as adding properties or methods, or collecting instances of a class. In the following example, we add a `toString` method that converts the class into a string representation. ```typescript type Constructor = new (...args: any[]) => T; function toString( Value: Class, context: ClassDecoratorContext ) { return class extends Value { constructor(...args: any[]) { super(...args); console.log(JSON.stringify(this)); console.log(JSON.stringify(context)); } }; } @toString class Person { name: string; constructor(name: string) { this.name = name; } greet() { return 'Hello, ' + this.name; } } const person = new Person('Simon'); /* Logs: {"name":"Simon"} {"kind":"class","name":"Person"} */ ``` #### Property Decorator Property decorators are useful for modifying the behavior of a property, such as changing the initialization values. In the following code, we have a script that sets a property to always be in uppercase: ```typescript function upperCase( target: undefined, context: ClassFieldDecoratorContext ) { return function (this: T, value: string) { return value.toUpperCase(); }; } class MyClass { @upperCase prop1 = 'hello!'; } console.log(new MyClass().prop1); // Logs: HELLO! ``` #### Method Decorator Method decorators allow you to change or enhance the behavior of methods. Below is an example of a simple logger: ```typescript function log( target: (this: This, ...args: Args) => Return, context: ClassMethodDecoratorContext< This, (this: This, ...args: Args) => Return > ) { const methodName = String(context.name); function replacementMethod(this: This, ...args: Args): Return { console.log(`LOG: Entering method '${methodName}'.`); const result = target.call(this, ...args); console.log(`LOG: Exiting method '${methodName}'.`); return result; } return replacementMethod; } class MyClass { @log sayHello() { console.log('Hello!'); } } new MyClass().sayHello(); ``` It logs: ```shell LOG: Entering method 'sayHello'. Hello! LOG: Exiting method 'sayHello'. ``` #### Getter and Setter Decorators Getter and setter decorators allow you to change or enhance the behavior of class accessors. They are useful, for instance, for validating property assignments. Here's a simple example for a getter decorator: ```typescript function range(min: number, max: number) { return function ( target: (this: This) => Return, context: ClassGetterDecoratorContext ) { return function (this: This): Return { const value = target.call(this); if (value < min || value > max) { throw 'Invalid'; } Object.defineProperty(this, context.name, { value, enumerable: true, }); return value; }; }; } class MyClass { private _value = 0; constructor(value: number) { this._value = value; } @range(1, 100) get getValue(): number { return this._value; } } const obj = new MyClass(10); console.log(obj.getValue); // Valid: 10 const obj2 = new MyClass(999); console.log(obj2.getValue); // Throw: Invalid! ``` #### Decorator Metadata Decorator Metadata simplifies the process for decorators to apply and utilize metadata in any class. They can access a new metadata property on the context object, which can serve as a key for both primitives and objects. Metadata information can be accessed on the class via `Symbol.metadata`. Metadata can be used for various purposes, such as debugging, serialization, or dependency injection with decorators. ```typescript //@ts-ignore Symbol.metadata ??= Symbol('Symbol.metadata'); // Simple polify type Context = | ClassFieldDecoratorContext | ClassAccessorDecoratorContext | ClassMethodDecoratorContext; // Context contains property metadata: DecoratorMetadata function setMetadata(_target: any, context: Context) { // Set the metadata object with a primitive value context.metadata[context.name] = true; } class MyClass { @setMetadata a = 123; @setMetadata accessor b = 'b'; @setMetadata fn() {} } const metadata = MyClass[Symbol.metadata]; // Get metadata information console.log(JSON.stringify(metadata)); // {"bar":true,"baz":true,"foo":true} ``` ### Inheritance Inheritance refers to the mechanism by which a class can inherit properties and methods from another class, known as the base class or superclass. The derived class, also called the child class or subclass, can extend and specialize the functionality of the base class by adding new properties and methods or overriding existing ones. ```typescript class Animal { name: string; constructor(name: string) { this.name = name; } speak(): void { console.log('The animal makes a sound'); } } class Dog extends Animal { breed: string; constructor(name: string, breed: string) { super(name); this.breed = breed; } speak(): void { console.log('Woof! Woof!'); } } // Create an instance of the base class const animal = new Animal('Generic Animal'); animal.speak(); // The animal makes a sound // Create an instance of the derived class const dog = new Dog('Max', 'Labrador'); dog.speak(); // Woof! Woof!" ``` TypeScript does not support multiple inheritance in the traditional sense and instead allows inheritance from a single base class. TypeScript supports multiple interfaces. An interface can define a contract for the structure of an object, and a class can implement multiple interfaces. This allows a class to inherit behavior and structure from multiple sources. ```typescript interface Flyable { fly(): void; } interface Swimmable { swim(): void; } class FlyingFish implements Flyable, Swimmable { fly() { console.log('Flying...'); } swim() { console.log('Swimming...'); } } const flyingFish = new FlyingFish(); flyingFish.fly(); flyingFish.swim(); ``` The `class` keyword in TypeScript, similar to JavaScript, is often referred to as syntactic sugar. It was introduced in ECMAScript 2015 (ES6) to offer a more familiar syntax for creating and working with objects in a class-based manner. However, it's important to note that TypeScript, being a superset of JavaScript, ultimately compiles down to JavaScript, which remains prototype-based at its core. ### Statics TypeScript has static members. To access the static members of a class, you can use the class name followed by a dot, without the need to create an object. ```typescript class OfficeWorker { static memberCount: number = 0; constructor(private name: string) { OfficeWorker.memberCount++; } } const w1 = new OfficeWorker('James'); const w2 = new OfficeWorker('Simon'); const total = OfficeWorker.memberCount; console.log(total); // 2 ``` ### Property initialization There are several ways how you can initialize properties for a class in TypeScript: Inline: In the following example these initial values will be used when an instance of the class is created. ```typescript class MyClass { property1: string = 'default value'; property2: number = 42; } ``` In the constructor: ```typescript class MyClass { property1: string; property2: number; constructor() { this.property1 = 'default value'; this.property2 = 42; } } ``` Using constructor parameters: ```typescript class MyClass { constructor( private property1: string = 'default value', public property2: number = 42 ) { // There is no need to assign the values to the properties explicitly. } log() { console.log(this.property2); } } const x = new MyClass(); x.log(); ``` ### Method overloading Method overloading allows a class to have multiple methods with the same name but different parameter types or a different number of parameters. This allows us to call a method in different ways based on the arguments passed. ```typescript class MyClass { add(a: number, b: number): number; // Overload signature 1 add(a: string, b: string): string; // Overload signature 2 add(a: number | string, b: number | string): number | string { if (typeof a === 'number' && typeof b === 'number') { return a + b; } if (typeof a === 'string' && typeof b === 'string') { return a.concat(b); } throw new Error('Invalid arguments'); } } const r = new MyClass(); console.log(r.add(10, 5)); // Logs 15 ``` ## Generics Generics allow you to create reusable components and functions that can work with multiple types. With generics, you can parameterize types, functions, and interfaces, allowing them to operate on different types without explicitly specifying them beforehand. Generics allow you to make code more flexible and reusable. ### Generic Type To define a generic type, you use angle brackets (`<>`) to specify the type parameters, for instance: ```typescript function identity(arg: T): T { return arg; } const a = identity('x'); const b = identity(123); const getLen = (data: ReadonlyArray) => data.length; const len = getLen([1, 2, 3]); ``` ### Generic Classes Generics can be applied also to classes, in this way they can work with multiple types by using type parameters. This is useful to create reusable class definitions that can operate on different data types while maintaining type safety. ```typescript class Container { private item: T; constructor(item: T) { this.item = item; } getItem(): T { return this.item; } } const numberContainer = new Container(123); console.log(numberContainer.getItem()); // 123 const stringContainer = new Container('hello'); console.log(stringContainer.getItem()); // hello ``` ### Generic Constraints Generic parameters can be constrained using the `extends` keyword followed by a type or interface that the type parameter must satisfy. In the following example T it is must containing a properly `length` in order to be valid: ```typescript const printLen = (value: T): void => { console.log(value.length); }; printLen('Hello'); // 5 printLen([1, 2, 3]); // 3 printLen({ length: 10 }); // 10 printLen(123); // Invalid ``` An interesting feature of generic introduced in version 3.4 RC is Higher order function type inference which introduced propagated generic type arguments: ```typescript declare function pipe( ab: (...args: A) => B, bc: (b: B) => C ): (...args: A) => C; declare function list(a: T): T[]; declare function box(x: V): { value: V }; const listBox = pipe(list, box); // (a: T) => { value: T[] } const boxList = pipe(box, list); // (x: V) => { value: V }[] ``` This functionality allows more easily typed safe pointfree style programming which is common in functional programming. ### Generic contextual narrowing Contextual narrowing for generics is the mechanism in TypeScript that allows the compiler to narrow down the type of a generic parameter based on the context in which it is used, it is useful when working with generic types in conditional statements: ```typescript function process(value: T): void { if (typeof value === 'string') { // Value is narrowed down to type 'string' console.log(value.length); } else if (typeof value === 'number') { // Value is narrowed down to type 'number' console.log(value.toFixed(2)); } } process('hello'); // 5 process(3.14159); // 3.14 ``` ## Erased Structural Types In TypeScript, objects do not have to match a specific, exact type. For instance, if we create an object that fulfills an interface's requirements, we can utilize that object in places where that interface is required, even if there was no explicit connection between them. Example: ```typescript type NameProp1 = { prop1: string; }; function log(x: NameProp1) { console.log(x.prop1); } const obj = { prop2: 123, prop1: 'Origin', }; log(obj); // Valid ``` ## Namespacing In TypeScript, namespaces are used to organize code into logical containers, preventing naming collisions and providing a way to group related code together. The usage of the `export` keywords allows access to the namespace in "outside" modules. ```typescript export namespace MyNamespace { export interface MyInterface1 { prop1: boolean; } export interface MyInterface2 { prop2: string; } } const a: MyNamespace.MyInterface1 = { prop1: true, }; ``` ## Symbols Symbols are a primitive data type that represents an immutable value which is guaranteed to be globally unique throughout the lifetime of the program. Symbols can be used as keys for object properties and provide a way to create non-enumerable properties. ```typescript const key1: symbol = Symbol('key1'); const key2: symbol = Symbol('key2'); const obj = { [key1]: 'value 1', [key2]: 'value 2', }; console.log(obj[key1]); // value 1 console.log(obj[key2]); // value 2 ``` In WeakMaps and WeakSets, symbols are now permissible as keys. ## Triple-Slash Directives Triple-slash directives are special comments that provide instructions to the compiler about how to process a file. These directives begin with three consecutive slashes (`///`) and are typically placed at the top of a TypeScript file and have no effects on the runtime behavior. Triple-slash directives are used to reference external dependencies, specify module loading behavior, enable/disable certain compiler features, and more. Few examples: Referencing a declaration file: ```typescript /// ``` Indicate the module format: ```typescript /// ``` Enable compiler options, in the following example strict mode: ```typescript /// ``` ## Type Manipulation ### Creating Types from Types Is it possible to create new types composing, manipulating or transforming existing types. Intersection Types (`&`): Allow you to combine multiple types into a single type: ```typescript type A = { foo: number }; type B = { bar: string }; type C = A & B; // Intersection of A and B const obj: C = { foo: 42, bar: 'hello' }; ``` Union Types (`|`): Allow you to define a type that can be one of several types: ```typescript type Result = string | number; const value1: Result = 'hello'; const value2: Result = 42; ``` Mapped Types: Allow you to transform the properties of an existing type to create new type: ```typescript type Mutable = { readonly [P in keyof T]: T[P]; }; type Person = { name: string; age: number; }; type ImmutablePerson = Mutable; // Properties become read-only ``` Conditional types: Allow you to create types based on some conditions: ```typescript type ExtractParam = T extends (param: infer P) => any ? P : never; type MyFunction = (name: string) => number; type ParamType = ExtractParam; // string ``` ### Indexed Access Types In TypeScript is it possible to access and manipulate the types of properties within another type using an index, `Type[Key]`. ```typescript type Person = { name: string; age: number; }; type AgeType = Person['age']; // number ``` ```typescript type MyTuple = [string, number, boolean]; type MyType = MyTuple[2]; // boolean ``` ### Utility Types Several built-in utility types can be used to manipulate types, below a list of the most common used: #### Awaited\ Constructs a type that recursively unwraps Promise types. ```typescript type A = Awaited>; // string ``` #### Partial\ Constructs a type with all properties of T set to optional. ```typescript type Person = { name: string; age: number; }; type A = Partial; // { name?: string | undefined; age?: number | undefined; } ``` #### Required\ Constructs a type with all properties of T set to required. ```typescript type Person = { name?: string; age?: number; }; type A = Required; // { name: string; age: number; } ``` #### Readonly\ Constructs a type with all properties of T set to readonly. ```typescript type Person = { name: string; age: number; }; type A = Readonly; const a: A = { name: 'Simon', age: 17 }; a.name = 'John'; // Invalid ``` #### Record\ Constructs a type with a set of properties K of type T. ```typescript type Product = { name: string; price: number; }; const products: Record = { apple: { name: 'Apple', price: 0.5 }, banana: { name: 'Banana', price: 0.25 }, }; console.log(products.apple); // { name: 'Apple', price: 0.5 } ``` #### Pick\ Constructs a type by picking the specified properties K from T. ```typescript type Product = { name: string; price: number; }; type Price = Pick; // { price: number; } ``` #### Omit\ Constructs a type by omitting the specified properties K from T. ```typescript type Product = { name: string; price: number; }; type Name = Omit; // { name: string; } ``` #### Exclude\ Constructs a type by excluding all values of type U from T. ```typescript type Union = 'a' | 'b' | 'c'; type MyType = Exclude; // b ``` #### Extract\ Constructs a type by extracting all values of type U from T. ```typescript type Union = 'a' | 'b' | 'c'; type MyType = Extract; // a | c ``` #### NonNullable\ Constructs a type by excluding null and undefined from T. ```typescript type Union = 'a' | null | undefined | 'b'; type MyType = NonNullable; // 'a' | 'b' ``` #### Parameters\ Extracts the parameter types of a function type T. ```typescript type Func = (a: string, b: number) => void; type MyType = Parameters; // [a: string, b: number] ``` #### ConstructorParameters\ Extracts the parameter types of a constructor function type T. ```typescript class Person { constructor( public name: string, public age: number ) {} } type PersonConstructorParams = ConstructorParameters; // [name: string, age: number] const params: PersonConstructorParams = ['John', 30]; const person = new Person(...params); console.log(person); // Person { name: 'John', age: 30 } ``` #### ReturnType\ Extracts the return type of a function type T. ```typescript type Func = (name: string) => number; type MyType = ReturnType; // number ``` #### InstanceType\ Extracts the instance type of a class type T. ```typescript class Person { name: string; constructor(name: string) { this.name = name; } sayHello() { console.log(`Hello, my name is ${this.name}!`); } } type PersonInstance = InstanceType; const person: PersonInstance = new Person('John'); person.sayHello(); // Hello, my name is John! ``` #### ThisParameterType\ Extracts the type of 'this' parameter from a function type T. ```typescript interface Person { name: string; greet(this: Person): void; } type PersonThisType = ThisParameterType; // Person ``` #### OmitThisParameter\ Removes the 'this' parameter from a function type T. ```typescript function capitalize(this: String) { return this[0].toUpperCase + this.substring(1).toLowerCase(); } type CapitalizeType = OmitThisParameter; // () => string ``` #### ThisType\ Servers as a market for a contextual `this` type. ```typescript type Logger = { log: (error: string) => void; }; let helperFunctions: { [name: string]: Function } & ThisType = { hello: function () { this.log('some error'); // Valid as "log" is a part of "this". this.update(); // Invalid }, }; ``` #### Uppercase\ Make uppercase the name of the input type T. ```typescript type MyType = Uppercase<'abc'>; // "ABC" ``` #### Lowercase\ Make lowercase the name of the input type T. ```typescript type MyType = Lowercase<'ABC'>; // "abc" ``` #### Capitalize\ Capitalize the name of the input type T. ```typescript type MyType = Capitalize<'abc'>; // "Abc" ``` #### Uncapitalize\ Uncapitalize the name of the input type T. ```typescript type MyType = Uncapitalize<'Abc'>; // "abc" ``` #### NoInfer\ NoInfer is a utility type designed to block the automatic inference of types within the scope of a generic function. Example: ```typescript // Automatic inference of types within the scope of a generic function. function fn(x: T[], y: T) { return x.concat(y); } const r = fn(['a', 'b'], 'c'); // Type here is ("a" | "b" | "c")[] ``` With NoInfer: ```typescript // Example function that uses NoInfer to prevent type inference function fn2(x: T[], y: NoInfer) { return x.concat(y); } const r2 = fn2(['a', 'b'], 'c'); // Error: Type Argument of type '"c"' is not assignable to parameter of type '"a" | "b"'. ``` ## Others ### Errors and Exception Handling TypeScript allows you to catch and handle errors using standard JavaScript error handling mechanisms: Try-Catch-Finally Blocks: ```typescript try { // Code that might throw an error } catch (error) { // Handle the error } finally { // Code that always executes, finally is optional } ``` You can also handle different types of error: ```typescript try { // Code that might throw different types of errors } catch (error) { if (error instanceof TypeError) { // Handle TypeError } else if (error instanceof RangeError) { // Handle RangeError } else { // Handle other errors } } ``` Custom Error Types: It is possible to specify more specific error by extending on the Error `class`: ```typescript class CustomError extends Error { constructor(message: string) { super(message); this.name = 'CustomError'; } } throw new CustomError('This is a custom error.'); ``` ### Mixin classes Mixin classes allow you to combine and compose behavior from multiple classes into a single class. They provide a way to reuse and extend functionality without the need for deep inheritance chains. ```typescript abstract class Identifiable { name: string = ''; logId() { console.log('id:', this.name); } } abstract class Selectable { selected: boolean = false; select() { this.selected = true; console.log('Select'); } deselect() { this.selected = false; console.log('Deselect'); } } class MyClass { constructor() {} } // Extend MyClass to include the behavior of Identifiable and Selectable interface MyClass extends Identifiable, Selectable {} // Function to apply mixins to a class function applyMixins(source: any, baseCtors: any[]) { baseCtors.forEach(baseCtor => { Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => { let descriptor = Object.getOwnPropertyDescriptor( baseCtor.prototype, name ); if (descriptor) { Object.defineProperty(source.prototype, name, descriptor); } }); }); } // Apply the mixins to MyClass applyMixins(MyClass, [Identifiable, Selectable]); let o = new MyClass(); o.name = 'abc'; o.logId(); o.select(); ``` ### Asynchronous Language Features As TypeScript is a superset of JavaScript, it has built-in asynchronous language features of JavaScript as: Promises: Promises are a way to handle asynchronous operations and their results using methods like `.then()` and `.catch()` to handle success and error conditions. To learn more: [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) Async/await: Async/await keywords are a way to provide a more synchronous-looking syntax for working with Promises. The `async` keyword is used to define an asynchronous function, and the `await` keyword is used within an async function to pause execution until a Promise is resolved or rejected. To learn more: [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function) [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await) The following API are well supported in TypeScript: Fetch API: [https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) Web Workers: [https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API) Shared Workers: [https://developer.mozilla.org/en-US/docs/Web/API/SharedWorker](https://developer.mozilla.org/en-US/docs/Web/API/SharedWorker) WebSocket: [https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API) ### Iterators and Generators Both Iterators and Generators are well supported in TypeScript. Iterators are objects that implement the iterator protocol, providing a way to access elements of a collection or sequence one by one. It is a structure that contains a pointer to the next element in the iteration. They have a `next()` method that returns the next value in the sequence along with a boolean indicating if the sequence is `done`. ```typescript class NumberIterator implements Iterable { private current: number; constructor( private start: number, private end: number ) { this.current = start; } public next(): IteratorResult { if (this.current <= this.end) { const value = this.current; this.current++; return { value, done: false }; } else { return { value: undefined, done: true }; } } [Symbol.iterator](): Iterator { return this; } } const iterator = new NumberIterator(1, 3); for (const num of iterator) { console.log(num); } ``` Generators are special functions defined using the `function*` syntax that simplifies the creation of iterators. They use the `yield` keyword to define the sequence of values and automatically pause and resume execution when values are requested. Generators make it easier to create iterators and are especially useful for working with large or infinite sequences. Example: ```typescript function* numberGenerator(start: number, end: number): Generator { for (let i = start; i <= end; i++) { yield i; } } const generator = numberGenerator(1, 5); for (const num of generator) { console.log(num); } ``` TypeScript also supports async iterators and async Generators. To learn more: [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Generator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Generator) [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Iterator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Iterator) ### TsDocs JSDoc Reference When working with a JavaScript code base, it is possible to help TypeScript to infer the right Type by using JSDoc comments with additional annotation to provide type information. Example: ```typescript /** * Computes the power of a given number * @constructor * @param {number} base – The base value of the expression * @param {number} exponent – The exponent value of the expression */ function power(base: number, exponent: number) { return Math.pow(base, exponent); } power(10, 2); // function power(base: number, exponent: number): number ``` Full documentation is provided to this link: [https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html](https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html) From version 3.7 it is possible to generate .d.ts type definitions from JavaScript JSDoc syntax. More information can be found here: [https://www.typescriptlang.org/docs/handbook/declaration-files/dts-from-js.html](https://www.typescriptlang.org/docs/handbook/declaration-files/dts-from-js.html) ### @types Packages under the @types organization are special package naming conventions used to provide type definitions for existing JavaScript libraries or modules. For instance using: ```shell npm install --save-dev @types/lodash ``` Will install the type definitions of `lodash` in your current project. To contribute to the type definitions of @types package, please submit a pull request to [https://github.com/DefinitelyTyped/DefinitelyTyped](https://github.com/DefinitelyTyped/DefinitelyTyped). ### JSX JSX (JavaScript XML) is an extension to the JavaScript language syntax that allows you to write HTML-like code within your JavaScript or TypeScript files. It is commonly used in React to define the HTML structure. TypeScript extends the capabilities of JSX by providing type checking and static analysis. To use JSX you need to set the `jsx` compiler option in your `tsconfig.json` file. Two common configuration options: * "preserve": emit .jsx files with the JSX unchanged. This option tells TypeScript to keep the JSX syntax as-is and not transform it during the compilation process. You can use this option if you have a separate tool, like Babel, that handles the transformation. * "react": enables TypeScript's built-in JSX transformation. React.createElement will be used. All options are available here: [https://www.typescriptlang.org/tsconfig#jsx](https://www.typescriptlang.org/tsconfig#jsx) ### ES6 Modules TypeScript does support ES6 (ECMAScript 2015) and many subsequent versions. This means you can use ES6 syntax, such as arrow functions, template literals, classes, modules, destructuring, and more. To enable ES6 features in your project, you can specify the `target` property in the tsconfig.json. A configuration example: ```json { "compilerOptions": { "target": "es6", "module": "es6", "moduleResolution": "node", "sourceMap": true, "outDir": "dist" }, "include": ["src"] } ``` ### ES7 Exponentiation Operator The exponentiation (`**`) operator computes the value obtained by raising the first operand to the power of the second operand. It functions similarly to `Math.pow()`, but with the added capability of accepting BigInts as operands. TypeScript fully supports this operator using as `target` in your tsconfig.json file `es2016` or larger version. ```typescript console.log(2 ** (2 ** 2)); // 16 ``` ### The for-await-of Statement This is a JavaScript feature fully supported in TypeScript which allows you to iterate over asynchronous iterable objects from target version es2018. ```typescript async function* asyncNumbers(): AsyncIterableIterator { yield Promise.resolve(1); yield Promise.resolve(2); yield Promise.resolve(3); } (async () => { for await (const num of asyncNumbers()) { console.log(num); } })(); ``` ### New target meta-property You can use in TypeScript the `new.target` meta-property which enables you to determine if a function or constructor was invoked using the new operator. It allows you to detect whether an object was created as a result of a constructor call. ```typescript class Parent { constructor() { console.log(new.target); // Logs the constructor function used to create an instance } } class Child extends Parent { constructor() { super(); } } const parentX = new Parent(); // [Function: Parent] const child = new Child(); // [Function: Child] ``` ### Dynamic Import Expressions It is possible to conditionally load modules or lazy load them on-demand using the ECMAScript proposal for dynamic import which is supported in TypeScript. The syntax for dynamic import expressions in TypeScript is as follows: ```typescript async function renderWidget() { const container = document.getElementById('widget'); if (container !== null) { const widget = await import('./widget'); // Dynamic import widget.render(container); } } renderWidget(); ``` ### "tsc –watch" This command starts a TypeScript compiler with `--watch` parameter, with the ability to automatically recompile TypeScript files whenever they are modified. ```shell tsc --watch ``` Starting from TypeScript version 4.9, file monitoring primarily relies on file system events, automatically resorting to polling if an event-based watcher cannot be established. ### Non-null Assertion Operator The Non-null Assertion Operator (Postfix !) also called Definite Assignment Assertions is a TypeScript feature that allows you to assert that a variable or property is not null or undefined, even if TypeScript's static type analysis suggests that it might be. With this feature it is possible to remove any explicit checking. ```typescript type Person = { name: string; }; const printName = (person?: Person) => { console.log(`Name is ${person!.name}`); }; ``` ### Defaulted declarations Defaulted declarations are used when a variable or parameter is assigned a default value. This means that if no value is provided for that variable or parameter, the default value will be used instead. ```typescript function greet(name: string = 'Anonymous'): void { console.log(`Hello, ${name}!`); } greet(); // Hello, Anonymous! greet('John'); // Hello, John! ``` ### Optional Chaining The optional chaining operator `?.` works like the regular dot operator (`.`) for accessing properties or methods. However, it gracefully handles null or undefined values by terminating the expression and returning `undefined`, instead of throwing an error. ```typescript type Person = { name: string; age?: number; address?: { street?: string; city?: string; }; }; const person: Person = { name: 'John', }; console.log(person.address?.city); // undefined ``` ### Nullish coalescing operator The nullish coalescing operator `??` returns the right-hand side value if the left-hand side is `null` or `undefined`; otherwise, it returns the left-hand side value. ```typescript const foo = null ?? 'foo'; console.log(foo); // foo const baz = 1 ?? 'baz'; const baz2 = 0 ?? 'baz'; console.log(baz); // 1 console.log(baz2); // 0 ``` ### Template Literal Types Template Literal Types allow to manipulate string value at type level and generate new string types based on existing ones. They are useful to create more expressive and precise types from string-based operations. ```typescript type Department = 'engineering' | 'hr'; type Language = 'english' | 'spanish'; type Id = `${Department}-${Language}-id`; // "engineering-english-id" | "engineering-spanish-id" | "hr-english-id" | "hr-spanish-id" ``` ### Function overloading Function overloading allows you to define multiple function signatures for the same function name, each with different parameter types and return type. When you call an overloaded function, TypeScript uses the provided arguments to determine the correct function signature: ```typescript function makeGreeting(name: string): string; function makeGreeting(names: string[]): string[]; function makeGreeting(person: unknown): unknown { if (typeof person === 'string') { return `Hi ${person}!`; } else if (Array.isArray(person)) { return person.map(name => `Hi, ${name}!`); } throw new Error('Unable to greet'); } makeGreeting('Simon'); makeGreeting(['Simone', 'John']); ``` ### Recursive Types A Recursive Type is a type that can refer to itself. This is useful for defining data structures that have a hierarchical or recursive structure (potentially infinite nesting), such as linked lists, trees, and graphs. ```typescript type ListNode = { data: T; next: ListNode | undefined; }; ``` ### Recursive Conditional Types It is possible to define complex type relationships using logic and recursion in TypeScript. Let’s break it down in simple terms: Conditional Types: allows you to define types based on boolean conditions: ```typescript type CheckNumber = T extends number ? 'Number' : 'Not a number'; type A = CheckNumber<123>; // 'Number' type B = CheckNumber<'abc'>; // 'Not a number' ``` Recursion: means a type definition that refers to itself within its own definition: ```typescript type Json = string | number | boolean | null | Json[] | { [key: string]: Json }; const data: Json = { prop1: true, prop2: 'prop2', prop3: { prop4: [], }, }; ``` Recursive Conditional Types combine both conditional logic and recursion. It means that a type definition can depend on itself through conditional logic, creating complex and flexible type relationships. ```typescript type Flatten = T extends Array ? Flatten : T; type NestedArray = [1, [2, [3, 4], 5], 6]; type FlattenedArray = Flatten; // 2 | 3 | 4 | 5 | 1 | 6 ``` ### ECMAScript Module Support in Node Node.js added support for ECMAScript Modules starting from version 15.3.0, and TypeScript has had ECMAScript Module Support for Node.js since version 4.7. This support can be enabled by using the `module` property with the value `nodenext` in the tsconfig.json file. Here's an example: ```json { "compilerOptions": { "module": "nodenext", "outDir": "./lib", "declaration": true } } ``` Node.js supports two file extensions for modules: `.mjs` for ES modules and `.cjs` for CommonJS modules. The equivalent file extensions in TypeScript are `.mts` for ES modules and `.cts` for CommonJS modules. When the TypeScript compiler transpiles these files to JavaScript, it will create `.mjs` and `.cjs` files. If you want to use ES modules in your project, you can set the `type` property to "module" in your package.json file. This instructs Node.js to treat the project as an ES module project. Additionally, TypeScript also supports type declarations in .d.ts files. These declaration files provide type information for libraries or modules written in TypeScript, allowing other developers to utilize them with TypeScript's type checking and auto-completion features. ### Assertion Functions In TypeScript, assertion functions are functions that indicate the verification of a specific condition based on their return value. In their simplest form, an assert function examines a provided predicate and raises an error when the predicate evaluates to false. ```typescript function isNumber(value: unknown): asserts value is number { if (typeof value !== 'number') { throw new Error('Not a number'); } } ``` Or can be declared as function expression: ```typescript type AssertIsNumber = (value: unknown) => asserts value is number; const isNumber: AssertIsNumber = value => { if (typeof value !== 'number') { throw new Error('Not a number'); } }; ``` Assertion functions share similarities with type guards. Type guards were initially introduced to perform runtime checks and ensure the type of a value within a specific scope. Specifically, a type guard is a function that evaluates a type predicate and returns a boolean value indicating whether the predicate is true or false. This differs slightly from assertion functions,where the intention is to throw an error rather than returning false when the predicate is not satisfied. Example of type guard: ```typescript const isNumber = (value: unknown): value is number => typeof value === 'number'; ``` ### Variadic Tuple Types Variadic Tuple Types are a features introduces in TypeScript version 4.0, let’s start to learn them by revise what is a tuple: A tuple type is an array which has a defined length, and were the type of each element is known: ```typescript type Student = [string, number]; const [name, age]: Student = ['Simone', 20]; ``` The term "variadic" means indefinite arity (accept a variable number of arguments). A variadic tuple is a tuple type which has all the property as before but the exact shape is not defined yet: ```typescript type Bar = [boolean, ...T, number]; type A = Bar<[boolean]>; // [boolean, boolean, number] type B = Bar<['a', 'b']>; // [boolean, 'a', 'b', number] type C = Bar<[]>; // [boolean, number] ``` In the previous code we can see that the tuple shape is defined by the `T` generic passed in. Variadic tuples can accept multiple generics make them very flexible: ```typescript type Bar = [...T, boolean, ...G]; type A = Bar<[number], [string]>; // [number, boolean, string] type B = Bar<['a', 'b'], [boolean]>; // ["a", "b", boolean, boolean] ``` With the new variadic tuples we can use: * The spreads in tuple type syntax can now be generic, so we can represent higher-order operation on tuples and arrays even when we do not know the actual types we are operating over. * The rest elements can occur anywhere in a tuple. Example: ```typescript type Items = readonly unknown[]; function concat( arr1: T, arr2: U ): [...T, ...U] { return [...arr1, ...arr2]; } concat([1, 2, 3], ['4', '5', '6']); // [1, 2, 3, "4", "5", "6"] ``` ### Boxed types Boxed types refer to the wrapper objects that are used to represent primitive types as objects. These wrapper objects provide additional functionality and methods that are not available directly on the primitive values. When you access a method like `charAt` or `normalize` on a `string` primitive, JavaScript wraps it in a `String` object, calls the method, and then throws the object away. Demonstration: ```typescript const originalNormalize = String.prototype.normalize; String.prototype.normalize = function () { console.log(this, typeof this); return originalNormalize.call(this); }; console.log('\u0041'.normalize()); ``` TypeScript represents this differentiation by providing separate types for the primitives and their corresponding object wrappers: * string => String * number => Number * boolean => Boolean * symbol => Symbol * bigint => BigInt The boxed types are usually not needed. Avoid using boxed types and instead use type for the primitives, for instance `string` instead of `String`. ### Covariance and Contravariance in TypeScript Covariance and Contravariance are used to describe how relationships work when dealing with inheritance or assignment of types. Covariance means that a type relationship preserves the direction of inheritance or assignment, so if a type A is a subtype of type B, then an array of type A is also considered a subtype of an array of type B. The important thing to note here is that the subtype relationship is maintained this means that Covariance accept subtype but doesn't accept supertype. Contravariance means that a type relationship reverses the direction of inheritance or assignment, so if a type A is a subtype of type B, then an array of type B is considered a subtype of an array of type A. The subtype relationship is reversed this means that Contravariance accept supertype but doesn't accept subtype. Notes: Bivariance means accept both supertype & subtype. Example: Let's say we have a space for all animals and a separate space just for dogs. In Covariance, you can put all the dogs in the animals space because dogs are a type of animal. But you cannot put all the animals in the dog space because there might be other animals mixed in. In Contravariance, you cannot put all the animals in the dogs space because the animals space might contain other animals as well. However, you can put all the dogs in the animal space because all dogs are also animals. ```typescript // Covariance example class Animal { name: string; constructor(name: string) { this.name = name; } } class Dog extends Animal { breed: string; constructor(name: string, breed: string) { super(name); this.breed = breed; } } let animals: Animal[] = []; let dogs: Dog[] = []; // Covariance allows assigning subtype (Dog) array to supertype (Animal) array animals = dogs; dogs = animals; // Invalid: Type 'Animal[]' is not assignable to type 'Dog[]' // Contravariance example type Feed = (animal: T) => void; let feedAnimal: Feed = (animal: Animal) => { console.log(`Animal name: ${animal.name}`); }; let feedDog: Feed = (dog: Dog) => { console.log(`Dog name: ${dog.name}, Breed: ${dog.breed}`); }; // Contravariance allows assigning supertype (Animal) callback to subtype (Dog) callback feedDog = feedAnimal; feedAnimal = feedDog; // Invalid: Type 'Feed' is not assignable to type 'Feed'. ``` In TypeScript, type relationships for arrays are covariant, while type relationships for function parameters are contravariant. This means that TypeScript exhibits both covariance and contravariance, depending on the context. #### Optional Variance Annotations for Type Parameters As of TypeScript 4.7.0, we can use the `out` and `in` keywords to be specific about Variance annotation. For Covariant, use the `out` keyword: ```typescript type AnimalCallback = () => T; // T is Covariant here ``` And for Contravariant, use the `in` keyword: ```typescript type AnimalCallback = (value: T) => void; // T is Contravariance here ``` ### Template String Pattern Index Signatures Template string pattern index signatures allow us to define flexible index signatures using template string patterns. This feature enables us to create objects that can be indexed with specific patterns of string keys, providing more control and specificity when accessing and manipulating properties. TypeScript from version 4.4 allows index signatures for symbols and template string patterns. ```typescript const uniqueSymbol = Symbol('description'); type MyKeys = `key-${string}`; type MyObject = { [uniqueSymbol]: string; [key: MyKeys]: number; }; const obj: MyObject = { [uniqueSymbol]: 'Unique symbol key', 'key-a': 123, 'key-b': 456, }; console.log(obj[uniqueSymbol]); // Unique symbol key console.log(obj['key-a']); // 123 console.log(obj['key-b']); // 456 ``` ### The satisfies Operator The `satisfies` allows you to check if a given type satisfies a specific interface or condition. In other words, it ensures that a type has all the required properties and methods of a specific interface. It is a way to ensure a variable fits into a definition of a type. Here is an example: ```typescript type Columns = 'name' | 'nickName' | 'attributes'; type User = Record; // Type Annotation using `User` const user: User = { name: 'Simone', nickName: undefined, attributes: ['dev', 'admin'], }; // In the following lines, TypeScript won't be able to infer properly user.attributes?.map(console.log); // Property 'map' does not exist on type 'string | string[]'. Property 'map' does not exist on type 'string'. user.nickName; // string | string[] | undefined // Type assertion using `as` const user2 = { name: 'Simon', nickName: undefined, attributes: ['dev', 'admin'], } as User; // Here too, TypeScript won't be able to infer properly user2.attributes?.map(console.log); // Property 'map' does not exist on type 'string | string[]'. Property 'map' does not exist on type 'string'. user2.nickName; // string | string[] | undefined // Using `satisfies` operators we can properly infer the types now const user3 = { name: 'Simon', nickName: undefined, attributes: ['dev', 'admin'], } satisfies User; user3.attributes?.map(console.log); // TypeScript infers correctly: string[] user3.nickName; // TypeScript infers correctly: undefined ``` ### Type-Only Imports and Export Type-Only Imports and Export allows you to import or export types without importing or exporting the values or functions associated with those types. This can be useful for reducing the size of your bundle. To use type-only imports, you can use the `import type` keyword. TypeScript permits using both declaration and implementation file extensions (.ts, .mts, .cts, and .tsx) in type-only imports, regardless of `allowImportingTsExtensions` settings. For example: ```typescript import type { House } from './house.ts'; ``` The following are supported forms: ```typescript import type T from './mod'; import type { A, B } from './mod'; import type * as Types from './mod'; export type { T }; export type { T } from './mod'; ``` ### using declaration and Explicit Resource Management A `using` declaration is a block-scoped, immutable binding, similar to `const`, used for managing disposable resources. When initialized with a value, the `Symbol.dispose` method of that value is recorded and subsequently executed upon exiting the enclosing block scope. This is based on ECMAScript's Resource Management feature, which is useful for performing essential cleanup tasks after object creation, such as closing connections, deleting files, and releasing memory. Notes: * Due to its recent introduction in TypeScript version 5.2, most runtimes lack native support. You'll need polyfills for: `Symbol.dispose`, `Symbol.asyncDispose`, `DisposableStack`, `AsyncDisposableStack`, `SuppressedError`. * Additionally, you will need to configure your tsconfig.json as follows: ```json { "compilerOptions": { "target": "es2022", "lib": ["es2022", "esnext.disposable", "dom"] } } ``` Example: ```typescript //@ts-ignore Symbol.dispose ??= Symbol('Symbol.dispose'); // Simple polify const doWork = (): Disposable => { return { [Symbol.dispose]: () => { console.log('disposed'); }, }; }; console.log(1); { using work = doWork(); // Resource is declared console.log(2); } // Resource is disposed (e.g., `work[Symbol.dispose]()` is evaluated) console.log(3); ``` The code will log: ```shell 1 2 disposed 3 ``` A resource eligible for disposal must adhere to the `Disposable` interface: ```typescript // lib.esnext.disposable.d.ts interface Disposable { [Symbol.dispose](): void; } ``` The `using` declarations record resource disposal operations in a stack, ensuring they are disposed in reverse order of declaration: ```typescript { using j = getA(), y = getB(); using k = getC(); } // disposes `C`, then `B`, then `A`. ``` Resources are guaranteed to be disposed, even if subsequent code or exceptions occur. This may lead to disposal potentially throwing an exception, possibly suppressing another. To retain information on suppressed errors, a new native exception, `SuppressedError`, is introduced. #### await using declaration An `await using` declaration handles an asynchronously disposable resource. The value must have a `Symbol.asyncDispose` method, which will be awaited at the block's end. ```typescript async function doWorkAsync() { await using work = doWorkAsync(); // Resource is declared } // Resource is disposed (e.g., `await work[Symbol.asyncDispose]()` is evaluated) ``` For an asynchronously disposable resource, it must adhere to either the `Disposable` or `AsyncDisposable` interface: ```typescript // lib.esnext.disposable.d.ts interface AsyncDisposable { [Symbol.asyncDispose](): Promise; } ``` ```typescript //@ts-ignore Symbol.asyncDispose ??= Symbol('Symbol.asyncDispose'); // Simple polify class DatabaseConnection implements AsyncDisposable { // A method that is called when the object is disposed asynchronously [Symbol.asyncDispose]() { // Close the connection and return a promise return this.close(); } async close() { console.log('Closing the connection...'); await new Promise(resolve => setTimeout(resolve, 1000)); console.log('Connection closed.'); } } async function doWork() { // Create a new connection and dispose it asynchronously when it goes out of scope await using connection = new DatabaseConnection(); // Resource is declared console.log('Doing some work...'); } // Resource is disposed (e.g., `await connection[Symbol.asyncDispose]()` is evaluated) doWork(); ``` The code logs: ```shell Doing some work... Closing the connection... Connection closed. ``` The `using` and `await using` declarations are allowed in Statements: `for`, `for-in`, `for-of`, `for-await-of`, `switch`. ### Import Attributes TypeScript 5.3's Import Attributes (labels for imports) tell the runtime how to handle modules (JSON, etc.). This improves security by ensuring clear imports and aligns with Content Security Policy (CSP) for safer resource loading. TypeScript ensures they are valid but lets the runtime handle their interpretation for specific module handling. Example: ```typescript import config from './config.json' with { type: 'json' }; ``` with dynamic import: ```typescript const config = import('./config.json', { with: { type: 'json' } }); ``` ================================================ FILE: tools/.markdownlint.json ================================================ { "code-block-style": { "style": "fenced" }, "code-fence-style": { "style": "backtick" }, "emphasis-style": { "style": "asterisk" }, "fenced-code-language": { "allowed_languages": [ "shell", "json", "typescript", "javascript", "text" ], "language_only": true }, "heading-style": { "style": "atx" }, "hr-style": { "style": "---" }, "line-length": false, "no-duplicate-heading": { "siblings_only": true }, "ol-prefix": { "style": "ordered" }, "proper-names": { "code_blocks": false, "names": [ "TypeScript", "Ctrl", "JavaScript", "Markdown", "Node.js", "Visual Studio Code", "Microsoft", "LinkedIn", "GitHub", "Twitter", "Email", "IntelliSense", "ECMAScript" ] }, "required-headings": false, "strong-style": { "style": "asterisk" }, "ul-style": { "style": "asterisk" } } ================================================ FILE: tools/.nvmrc ================================================ v19.7.0 ================================================ FILE: tools/.prettierrc ================================================ { "trailingComma": "es5", "tabWidth": 4, "semi": true, "singleQuote": true, "printWidth": 80, "arrowParens": "avoid" } ================================================ FILE: tools/.vscode/launch.json ================================================ { "version": "0.2.0", "configurations": [ { "name": "Debug lint", "type": "node", "request": "launch", "runtimeExecutable": "node", "runtimeArgs": [ "--nolazy", "-r", "${workspaceRoot}/node_modules/ts-node/register/transpile-only" ], "args": [ "lint.ts", "--example", "hello" ], "cwd": "${workspaceRoot}", "internalConsoleOptions": "openOnSessionStart", "skipFiles": [ "/**", "node_modules/**" ] }, { "name": "Python: Current File", "type": "python", "request": "launch", "program": "${file}", "console": "integratedTerminal", "justMyCode": true } ] } ================================================ FILE: tools/.vscode/settings.json ================================================ { "editor.formatOnSave": true, } ================================================ FILE: tools/Makefile ================================================ format: npm run format check: npm run check website: python3 ./make-website-content.py website-deploy: cd ../website && npm run deploy website-preview: cd ../website && npm run build && npm run preview books: ./make-books.sh ================================================ FILE: tools/README.md ================================================ # Tools for Working with Markdown Books If you're working with Markdown books, here are some essential tools and commands to help you streamline your workflow. ## Installation Before you begin, ensure you have Node.js installed. To set up the required dependencies, use the following commands: In folder `tools` and `website`: ```shell nvm use npm install ``` ```shell brew install pandoc brew install epubcheck brew install --cask calibre ``` ## Commands Use `make` to run the main commands: * `make format`: Format Markdown files for books. * `make check`: Run several checks to ensure the Markdown files are valid. * `make website`: Create different Markdown pages for the website. * `make website-preview`: Build and preview website locally. * `make website-deploy`: Build and deploy website to GitHub Pages. * `make books`: Create .epub books. ### Formatting Consistent code formatting is crucial. To format all TypeScript snippets, we use Prettier. Execute the following command for formatting: ```shell npm run format ``` ### Compilation To compile TypeScript snippets within the Markdown files, utilize the following command: ```shell npm run compile ``` ### Linting To ensure that your Markdown files adhere to proper formatting rules, use the linting command: ```shell npm run lint:md ``` ### Linting and Formatting For a comprehensive process that includes linting all Markdown files, applying Prettier formatting to all TypeScript snippets, and compiling them using TypeScript, use the following command: ```shell npm run check ``` The project uses the [Markdown All in One](https://marketplace.visualstudio.com/items?itemName=yzhang.markdown-all-in-one)Visual Studio Code extension to automatically update the table of contents. Use the following command to install it: ```shell code --install-extension yzhang.markdown-all-in-one ``` ### Skipping Compilation If you have specific snippets in the Markdown files that you don't want to compile, simply add `` just before the TypeScript demarcation for those snippets. ### Epub Generation To generate Epub files from your Markdown books, navigate to the `tools` folder and run the following command: ```shell make-books.sh ``` After generating the Epub files, thoroughly test them, and once you're satisfied with the results, commit the changes. These tools will assist you in efficiently working with Markdown books and ensure a smooth and organized process. Happy writing! ## Debug EPUB Issues To debug the EPUB files you create, follow these steps: First, download and install Sigil. You can use the following command to install Sigil: ```shell brew install --cask sigil ``` Next, run an EPUB check to validate your EPUB file. For example: ```shell epubcheck ../downloads/typescript-book.epub ``` Finally, open the EPUB file using Sigil for detailed inspection. ## How to Contribute to This E-Book To contribute to this e-book, follow these steps: * Update the main Markdown file with your changes. * Run `make check` to ensure the linter and other checks pass. * Preview the website locally with `make website-preview` to make sure it works as expected. * Generate the e-books by running `make books`. * Submit your PR and share your awesome contributions! ================================================ FILE: tools/check_toc.py ================================================ import re import sys def slugify(text): text = text.lower() text = re.sub(r'[^\w\s-]', '', text) text = re.sub(r'\s+', '-', text) return text def check_toc(filepath): """ Checks if all links in the TOC section of a markdown file point to existing headers. """ with open(filepath, 'r', encoding='utf-8') as f: lines = f.readlines() headers = [] toc_links = [] # Simple state machine to detect TOC section if needed, # but for now we'll just scan all lines for headers and links. # Actually, we should only care about links that look like internal anchors (#...) for line in lines: line = line.strip() # Detect headers header_match = re.match(r'^(#+)\s+(.+)$', line) if header_match: title = header_match.group(2).strip() # Ignore some specific things if needed, but generally: anchor = slugify(title) headers.append(anchor) # Detect links # simple regex for markdown links [text](#anchor) # We perform findall because there might be multiple links in a line (though rare for TOC) links = re.findall(r'\[([^\]]+)\]\((#[^)]+)\)', line) for text, link in links: # clean the link (remove #) anchor_ref = link[1:] toc_links.append({'text': text, 'ref': anchor_ref}) # Verify broken_links = [] for link in toc_links: if link['ref'] not in headers: # Special case: sometimes headers might have duplicate names and some markdown parsers handle it. # But normally GitHub appends -1, -2 etc. We might need to handle that if strict. # For now, let's just check existence. broken_links.append(link) return broken_links if __name__ == "__main__": if len(sys.argv) < 2: print("Usage: python3 check_toc.py ") sys.exit(1) filepath = sys.argv[1] broken = check_toc(filepath) if broken: print(f"Found {len(broken)} broken links:") for b in broken: print(f"- [{b['text']}](#{b['ref']}) -> Header not found (expected #{b['ref']})") else: print("No broken links found.") ================================================ FILE: tools/compile.ts ================================================ import * as fs from 'fs-extra'; import * as path from 'path'; import * as ts from "typescript"; import { marked } from 'marked'; import { pipe } from 'fp-ts/function' import { makeFilePath } from './utils'; import { languages } from './i18n'; const TEMP_DIR = 'temp' const SKIP_COMMENT = '' type ReportsInfo = Readonly<{ reports: ts.Diagnostic[], emitSkipped: boolean }> type TempFilePaths = ReadonlyArray type CodeSnippets = ReadonlyArray const exitScript = (emitSkipped: boolean) => { const ERROR_CODE = 1 const SUCCESS_CODE = 0 const exitCode = emitSkipped ? ERROR_CODE : SUCCESS_CODE; console.log(`Process exiting with code '${exitCode}'.`); exitCode === ERROR_CODE && process.exit(exitCode); } const compileAndReport = (options: ts.CompilerOptions) => (fileNames: TempFilePaths): ReportsInfo => { const program = ts.createProgram(fileNames, options); const emitResult = program.emit(); const reports = ts .getPreEmitDiagnostics(program) .concat(emitResult.diagnostics); return { reports, emitSkipped: emitResult.emitSkipped } } const logReports = (data: ReportsInfo) => { data.reports.forEach(diagnostic => { if (diagnostic.file) { const { line, character } = ts.getLineAndCharacterOfPosition(diagnostic.file, diagnostic.start!); const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n"); console.log(`${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}`); } else { console.log(ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n")); } }); return data.emitSkipped } const isTypeScriptCode = (token: marked.Token): token is marked.Tokens.Code => token.type === 'code' && token.lang === 'typescript'; const isSkipComment = (token: marked.Token): token is marked.Tokens.HTML => token.type === 'html' && token.text === `${SKIP_COMMENT}\n`; const extractCodeSnippets = (markdown: string): CodeSnippets => pipe( new marked.Lexer(), lexer => lexer.lex(markdown), tokens => { let skipIndex = -1 return tokens.reduce((tokens, token, index) => { if (index === skipIndex) { skipIndex = -1 return [...tokens] } if (isSkipComment(token)) { skipIndex = index + 1 return [...tokens] } return [...tokens, token] }, []) }, tokens => tokens.filter(isTypeScriptCode), codes => codes.map(x => x.text) ) const makeTempFiles = (snippets: CodeSnippets): TempFilePaths => snippets.reduce((acc, snippet, index) => { const tempFile = path.join(__dirname, `temp/temp_${index}.ts`); fs.writeFileSync(tempFile, snippet); return [ ...acc, tempFile ] }, []) const processMarkdownFile = (inputPath: string): void => pipe( () => fs.removeSync(TEMP_DIR), () => fs.ensureDirSync(TEMP_DIR), () => fs.readFileSync(inputPath, 'utf-8'), extractCodeSnippets, makeTempFiles, compileAndReport({ noEmitOnError: true, noImplicitAny: true, target: ts.ScriptTarget.ESNext, module: ts.ModuleKind.CommonJS, moduleDetection: ts.ModuleDetectionKind.Force, noUnusedLocals: false, strict: true }), logReports, exitScript, ) for (const item of languages) { console.log("Compiling: ", item) processMarkdownFile(makeFilePath(item)); } ================================================ FILE: tools/config.ts ================================================ export const PRETTIER_CONFIG_FILE_PATH = './.prettierrc' export const CODE_BLOCK_TS_REGEX = /```typescript([\s\S]*?)```/g; ================================================ FILE: tools/format.ts ================================================ import * as prettier from 'prettier'; import * as fs from 'fs'; import { Language, languages } from './i18n'; import { PrettierOptions, getPrettierOptions, makeFilePath } from './utils'; import { CODE_BLOCK_TS_REGEX } from './config'; const formatCodeBlocksInMarkdownFile = async (filePath: string, options: PrettierOptions, language: Language): Promise => { const markdown = await fs.promises.readFile(filePath, 'utf-8'); let formattedMarkdown = markdown; let match; while ((match = CODE_BLOCK_TS_REGEX.exec(markdown)) !== null) { const codeBlock = match[0]; const code = match[1].trim(); const formattedCode = await prettier.format(code, { parser: 'typescript', ...options, }); formattedMarkdown = formattedMarkdown.replace(codeBlock, `\`\`\`typescript\n${formattedCode}\`\`\``); } await fs.promises.writeFile(filePath, formattedMarkdown, 'utf-8'); console.log(`${language} Formatted code blocks have been updated in the file: ${filePath}`); } const main = async () => { const options = await getPrettierOptions() for (const language of languages) { formatCodeBlocksInMarkdownFile(makeFilePath(language), options, language); } } main() ================================================ FILE: tools/i18n.ts ================================================ export enum Language { en_EN = 'en_EN', zh_CN = 'zh_CN', it_IT = 'it_IT', pt_BR = 'pt_BR', sv_SE = 'sv_SE', } export type Languages = [Language.en_EN, Language.zh_CN, Language.it_IT, Language.pt_BR, Language.sv_SE] export const languages: Languages = [Language.en_EN, Language.zh_CN, Language.it_IT, Language.pt_BR, Language.sv_SE] ================================================ FILE: tools/lint.ts ================================================ import * as prettier from 'prettier'; import * as fs from 'fs'; import { Language, languages } from './i18n'; import { PrettierOptions, getPrettierOptions, makeFilePath } from './utils'; import { CODE_BLOCK_TS_REGEX } from './config'; const lintCodeBlocksInMarkdownFile = async (filePath: string, options: PrettierOptions, language: Language): Promise => { const markdown = await fs.promises.readFile(filePath, 'utf-8'); let reportSnippetsNotLinted = ''; let match; while ((match = CODE_BLOCK_TS_REGEX.exec(markdown)) !== null) { const code = match[1].trim() + '\n' const isCodeLinted = await prettier.check(code, { parser: 'typescript', ...options, }); if (isCodeLinted === false) { const lineNumber = markdown.substring(0, match.index).split('\n').length; reportSnippetsNotLinted += `Line ${lineNumber}:\n` reportSnippetsNotLinted += code reportSnippetsNotLinted += '\n' } } if (reportSnippetsNotLinted.length === 0) { console.log(`${language} All snippets are linted!`) } else { console.error(`${language} Not all snippets are linted! Please use this command to automatically format the snippets: npm run format\n`) console.error(reportSnippetsNotLinted) process.exit(1) } } const main = async () => { const options = await getPrettierOptions() for (const language of languages) { lintCodeBlocksInMarkdownFile(makeFilePath(language), options, language); } } main() ================================================ FILE: tools/make-books.sh ================================================ #!/bin/bash # This script creates eBooks from the TypeScript Book. DIR_DOWNLOADS="downloads" INPUT_EN="README" INPUT_CN="README-zh_CN" INPUT_IT="README-it_IT" INPUT_BR="README-pt_BR" INPUT_SE="README-sv_SE" OUTPUT_EN="typescript-book" OUTPUT_CN="typescript-book-zh_CN" OUTPUT_IT="typescript-book-it_IT" OUTPUT_BR="typescript-book-pt_BR" OUTPUT_SE="typescript-book-sv_SE" AUTHOR="Simone Poggiali" TITLE_EN="The Concise TypeScript Book" TITLE_CN="# 简洁的TypeScript之书" TITLE_IT="The Concise TypeScript Book" TITLE_BR="The Concise TypeScript Book" TITLE_SE="The Concise TypeScript Book" cd ../ echo "Making ebooks ..." # Check dependencies if command -v pandoc &>/dev/null; then echo "pandoc is installed" else echo "You need to install pandoc. Please visit: https://pandoc.org/installing.html" exit 1 fi if command -v epubcheck &>/dev/null; then echo "epubcheck is installed" else echo "You need to install epubcheck. Please visit: https://www.w3.org/publishing/epubcheck/docs/installation/" exit 1 fi if command -v ebook-convert &>/dev/null; then echo "calibre is installed" else echo "You need to install calibre. Please visit: https://calibre-ebook.com/download" exit 1 fi # Generate eBooks pandoc -o $DIR_DOWNLOADS/$OUTPUT_EN.epub --metadata title="$TITLE_EN" --metadata author="$AUTHOR" -s $INPUT_EN.md pandoc -o $DIR_DOWNLOADS/$OUTPUT_CN.epub --metadata title="$TITLE_CN" --metadata author="$AUTHOR" -s $INPUT_CN.md pandoc -o $DIR_DOWNLOADS/$OUTPUT_IT.epub --metadata title="$TITLE_IT" --metadata author="$AUTHOR" -s $INPUT_IT.md pandoc -o $DIR_DOWNLOADS/$OUTPUT_BR.epub --metadata title="$TITLE_BR" --metadata author="$AUTHOR" -s $INPUT_BR.md pandoc -o $DIR_DOWNLOADS/$OUTPUT_SE.epub --metadata title="$TITLE_SE" --metadata author="$AUTHOR" -s $INPUT_SE.md # Validate eBooks epubcheck $DIR_DOWNLOADS/$OUTPUT_EN.epub epubcheck $DIR_DOWNLOADS/$OUTPUT_CN.epub epubcheck $DIR_DOWNLOADS/$OUTPUT_IT.epub epubcheck $DIR_DOWNLOADS/$OUTPUT_BR.epub epubcheck $DIR_DOWNLOADS/$OUTPUT_SE.epub # Generate PDFs ebook-convert $DIR_DOWNLOADS/$OUTPUT_EN.epub $DIR_DOWNLOADS/$OUTPUT_EN.pdf --pdf-page-numbers ebook-convert $DIR_DOWNLOADS/$OUTPUT_CN.epub $DIR_DOWNLOADS/$OUTPUT_CN.pdf --pdf-page-numbers ebook-convert $DIR_DOWNLOADS/$OUTPUT_IT.epub $DIR_DOWNLOADS/$OUTPUT_IT.pdf --pdf-page-numbers ebook-convert $DIR_DOWNLOADS/$OUTPUT_BR.epub $DIR_DOWNLOADS/$OUTPUT_BR.pdf --pdf-page-numbers ebook-convert $DIR_DOWNLOADS/$OUTPUT_SE.epub $DIR_DOWNLOADS/$OUTPUT_SE.pdf --pdf-page-numbers echo "Books were created. Please commit!" ================================================ FILE: tools/make-website-content.py ================================================ """ Generate multiple Markdown documents from a single Markdown file by splitting it based on headings. This script is designed for creating pages on a website and provides results for multiple languages. Note: the number of headings per language must be the same. """ import os import re import shutil from typing import List # INPUT_FILE_PATH = "./test-md/README.md" # OUTPUT_DIR_PATH = "./test-md/en" # INPUT_FILE_PATH_CN = "./test-md/README-zh_CN.md" # OUTPUT_DIR_PATH_CN = "./test-md/zh-cn" INPUT_FILE_PATH = "../README.md" OUTPUT_DIR_PATH = "../website/src/content/docs/book" INPUT_FILE_PATH_CN = "../README-zh_CN.md" OUTPUT_DIR_PATH_CN = "../website/src/content/docs/zh-cn/book" INPUT_FILE_PATH_IT = "../README-it_IT.md" OUTPUT_DIR_PATH_IT = "../website/src/content/docs/it-it/book" INPUT_FILE_PATH_PT = "../README-pt_BR.md" OUTPUT_DIR_PATH_PT = "../website/src/content/docs/pt-br/book" INPUT_FILE_PATH_SV = "../README-sv_SE.md" OUTPUT_DIR_PATH_SV = "../website/src/content/docs/sv-se/book" def manage_output_dir(path: str) -> None: if os.path.exists(path): shutil.rmtree(path) os.makedirs(path) def read_content_file(path: str) -> List[str]: with open(path, "r") as file: lines = file.readlines() return lines def make_file_name(name: str) -> str: content_sanitized = re.sub(r"[^a-zA-Z0-9]+", "-", name.lower()).strip("-") return f"{content_sanitized}" def make_output_path(output_dir: str, file_name: str): return f"{output_dir}/{file_name}.md" def is_line_header_1_to_2(line: str) -> bool: return re.match(r"^(#{1,2})\s+(.+)", line) def make_file_output_path(output_dir: str, name: str) -> str: file_name = make_file_name(name) output_file_path = make_output_path(output_dir, file_name) return output_file_path def make_markdown_page_metadata(order: int, header: str) -> List[str]: return [ "---\n", f"title: {header}\n", "sidebar:\n", f" order: {order}\n", f" label: {order}. {header}\n", "---\n", "\n", ] def save_content_to_file(path: str, lines: List[str]): with open(path, "w") as output_file: output_file.writelines(lines) def save_pages_to_files( data_pages: List[List[str]], master_headers: List[str], output_dir: str ) -> None: for index, header in enumerate(master_headers): file = make_file_output_path(output_dir, header) save_content_to_file(file, data_pages[index]) def find_master_headers(lines: List[str]) -> List[str]: headers = [x for x in lines if is_line_header_1_to_2(x)] headers_clean = list(map(lambda x: make_file_name(x), headers)) return headers_clean def remove_markdown_anchors(markdown_text: str): pattern = r"\[(.*?)\]\(#[^\)]*\)" replacement = r"\1" transformed_text = re.sub(pattern, replacement, markdown_text) return transformed_text def split_content_by_headings(lines: List[str]): current_content = [] in_page = False header_index = -1 content_result = [] for line in lines: is_header_match = is_line_header_1_to_2(line) if is_header_match: header_text = is_header_match.group(2).strip() header_index += 1 if not in_page: in_page = True current_content.extend( make_markdown_page_metadata(header_index + 1, header_text) ) else: content_result.extend([current_content]) current_content = [] in_page = True current_content.extend( make_markdown_page_metadata(header_index + 1, header_text) ) else: line_new = remove_markdown_anchors(line) current_content.append(line_new) header_index += 1 content_result.extend([current_content]) return content_result def process(base_input_path, input_lang_path: str, base_output_path: str) -> None: manage_output_dir(base_output_path) content_lines_master = read_content_file(base_input_path) master_headers = find_master_headers(content_lines_master) content_lines = read_content_file(input_lang_path) data_pages = split_content_by_headings( content_lines, ) save_pages_to_files(data_pages, master_headers, base_output_path) print(f"A total of: {len(master_headers)} files were at: {base_output_path}") process(INPUT_FILE_PATH, INPUT_FILE_PATH, OUTPUT_DIR_PATH) process(INPUT_FILE_PATH, INPUT_FILE_PATH_CN, OUTPUT_DIR_PATH_CN) process(INPUT_FILE_PATH, INPUT_FILE_PATH_IT, OUTPUT_DIR_PATH_IT) process(INPUT_FILE_PATH, INPUT_FILE_PATH_PT, OUTPUT_DIR_PATH_PT) process(INPUT_FILE_PATH, INPUT_FILE_PATH_SV, OUTPUT_DIR_PATH_SV) ================================================ FILE: tools/package.json ================================================ { "name": "typescript-book", "version": "1.0.0", "description": "The Concise TypeScript Book provides a comprehensive and succinct overview of TypeScript's capabilities. It offers clear explanations covering all aspects found in the latest version of the language, from its powerful type system to advanced features. Whether you're a beginner or an experienced developer, this book is an invaluable resource to enhance your understanding and proficiency in TypeScript.", "main": "index.js", "scripts": { "format": "ts-node format", "compile": "ts-node compile", "test": "echo \"Error: no test specified\" && exit 1", "lint": "ts-node lint", "lint:md": "markdownlint \"./*.md\" markdownlint \"../*.md\"", "check": "npm run lint:md && npm run format && npm run compile" }, "author": "", "license": "ISC", "devDependencies": { "@types/fs-extra": "^11.0.1", "@types/marked": "^5.0.0", "@types/node": "^20.3.1", "@types/prettier": "^2.7.3", "fp-ts": "^2.16.0", "fs-extra": "^11.1.1", "markdownlint-cli": "^0.35.0", "marked": "^5.1.0", "prettier": "^3.3.0", "ts-node": "^10.9.1", "typescript": "^5.4.5" } } ================================================ FILE: tools/remove_skip_empty_lines.sh ================================================ #!/bin/bash # This script removes empty lines after comments in a markdown file. # Remove empty lines after comments sed -i '' '//{N;s/\n$//;}' README-it_IT.md echo "Empty lines after comments have been removed." ================================================ FILE: tools/test-md/README-zh_CN.md ================================================ # 简洁 TypeScript 书 TypeScript 有效开发简明指南。 **免费和开源**。 ## 创建文件和文件夹 可使用 按钮访问文件资源管理器。 ## 切换到另一个文件 您的所有文件和文件夹都在文件资源管理器中显示为树。 您可以通过单击树中的文件从一个文件切换到另一个文件。 ### 新标题 一些家长内容。 #### 嵌套内容 1 更多内容在这里。 ##### 嵌套内容 2 内容甚至更多。 ================================================ FILE: tools/test-md/README.md ================================================ # The Concise TypeScript Book A Concise Guide to Effective Development in TypeScript. **Free and Open Source**. ## Create files and folders The file explorer is accessible using the button. ## Switch to another file All your files and folders are presented as a tree in the file explorer. You can switch from one to another by clicking a file in the tree. ### New title Some parent content. #### Nested content 1 More content here. ##### Nested content 2 Even more content. ================================================ FILE: tools/tsconfig.json ================================================ { "compilerOptions": { "target": "es2022", "module": "commonjs", "noImplicitAny": true, "esModuleInterop": true, "noEmitOnError": true, "moduleDetection": "force", "noUnusedLocals": false, "forceConsistentCasingInFileNames": true, "strict": true, "skipLibCheck": true, "lib": [ "es2022", "esnext.disposable", "esnext.decorators", "dom" ] } } ================================================ FILE: tools/utilities-add-translation/README.md ================================================ # Markdown Split and Translation Tools This directory contains Python scripts for splitting, translating, and managing markdown documentation files. This is useful when you want to add a new language to the book. ## Scripts Overview ### 1. `split_markdown.py` Splits a large markdown file into smaller parts based on character count. **Usage:** ```bash # Split root README.md and save parts in current directory (default) python3 split_markdown.py # Split a specific file and save parts in current directory python3 split_markdown.py path/to/file.md # Split and save parts in specific directory python3 split_markdown.py ../../README.md ../../target_folder ``` **Output:** Creates files named `README-part-01.md`, `README-part-02.md`, etc. --- ### 2. `create_language_files.py` Automatically splits README.md into the target language folder and creates empty translation files. **Usage:** ```bash # Create Italian translation files (splits README.md into ../../ita) python3 create_language_files.py ITA # Create French translation files in specific folder python3 create_language_files.py FR ../../fra # Create Spanish translation files python3 create_language_files.py ES ../../spa ``` **Output:** 1. Splits README.md and saves parts in the target language folder 2. Creates empty translation files: `README-part-01-ITA.md`, `README-part-02-ITA.md`, etc. --- ### 3. `concatenate_language_files.py` Concatenates all translated part files into a single file. **Usage:** ```bash # Concatenate Italian files in ../../ita (default) python3 concatenate_language_files.py it_IT # Concatenate Spanish files in specific directory python3 concatenate_language_files.py es_ES ../../spa # Concatenate French files python3 concatenate_language_files.py fr_FR ../../fra ``` **Output:** Creates `README-it_IT.md`, `README-es_ES.md`, etc. containing all parts concatenated. --- ### 4. `verify_concatenation.py` Verifies that all part files were included in the concatenated file by comparing character counts. **Usage:** ```bash # Verify Italian files in ../../ita (default) python3 verify_concatenation.py ITA # Verify Spanish files in specific directory python3 verify_concatenation.py ES ../../spa ``` **Output:** Shows character count comparison and reports any mismatches. --- ### 5. `verify_concatenation_detailed.py` Enhanced verification that accounts for newlines added between files. **Usage:** ```bash # Verify Italian files in ../../ita (default) python3 verify_concatenation_detailed.py it_IT # Verify Spanish files in specific directory python3 verify_concatenation_detailed.py es_ES ../../spa ``` **Output:** Detailed analysis including expected newlines and precise character counts. --- ## Complete Workflow Example ### For Italian Translation: ```bash cd tools/split-md # Step 1: Create Italian folder, split README.md, and create translation files python3 create_language_files.py it_IT # Step 2: Translate each README-part-XX-it_IT.md file manually # Step 3: Concatenate all translated parts python3 concatenate_language_files.py it_IT # Step 4: Verify the concatenation python3 verify_concatenation_detailed.py it_IT ``` ### For Spanish Translation: ```bash cd tools/split-md # Step 1: Create Spanish folder, split README.md, and create translation files python3 create_language_files.py es_ES ../../spa # Step 2: Translate each README-part-XX-es_ES.md file manually # Step 3: Concatenate all translated parts python3 concatenate_language_files.py es_ES ../../spa # Step 4: Verify the concatenation python3 verify_concatenation_detailed.py es_ES ../../spa ``` --- ## File Naming Conventions - **Source parts:** `README-part-01.md`, `README-part-02.md`, ... - **Translation parts:** `README-part-01-{LANG}.md`, `README-part-02-{LANG}.md`, ... - **Final concatenated:** `README-{lang}_{COUNTRY}.md` (e.g., `README-it_IT.md`, `README-fr_FR.md`) Language codes follow ISO 639-1 (language) and ISO 3166-1 (country) standards. --- ## AI Assistant Prompt for Translation Workflow **Copy and paste this prompt to an AI assistant to automate the complete translation workflow:** ``` I need you to help me create a translation workflow for a TypeScript documentation book. Here's what I need you to do: 1. Navigate to the tools/split-md directory 2. Follow the complete workflow for creating a new language translation 3. Use language code [LANGUAGE_CODE] and target directory ../../[LANGUAGE_FOLDER] 4. Execute all steps including verification 5. Add sample translated content to the first 2-3 files for testing 6. Provide a detailed report of the results The workflow steps are: - Step 1: Run `python3 create_language_files.py [LANGUAGE_CODE] ../../[LANGUAGE_FOLDER]` - Step 2: Add sample translated content to first few files - Step 3: Run `python3 concatenate_language_files.py [LANGUAGE_CODE] ../../[LANGUAGE_FOLDER]` - Step 4: Run `python3 verify_concatenation_detailed.py [LANGUAGE_CODE] ../../[LANGUAGE_FOLDER]` Replace [LANGUAGE_CODE] with the target language code (e.g., DE, FR, ES, CZ, PT, RU, JA, KO) and [LANGUAGE_FOLDER] with the appropriate folder name (e.g., deu, fra, spa, cze, por, rus, jpn, kor). Expected results: - Multiple part files created in language folder (number depends on README.md size) - Equal number of empty translation files created - Sample content added to test files - Successful concatenation with proper ISO naming - Perfect verification match including newlines - All files organized in language-specific directory (not root) Please execute this workflow and provide a summary report of the results. ``` **Example usage:** - For German: Replace [LANGUAGE_CODE] with `de_DE` and [LANGUAGE_FOLDER] with `deu` - For Portuguese: Replace [LANGUAGE_CODE] with `pt_PT` and [LANGUAGE_FOLDER] with `por` - For Russian: Replace [LANGUAGE_CODE] with `ru_RU` and [LANGUAGE_FOLDER] with `rus` --- ## Requirements - Python 3.6+ - No external dependencies required --- ## Notes - All scripts are designed to run from the `tools/split-md` directory - Default paths assume the project structure: `typescript-book/tools/split-md/` - Maximum characters per split: 3,500 (configurable in `split_markdown.py`) - Concatenation adds newlines between files for proper formatting ================================================ FILE: tools/utilities-add-translation/concatenate_language_files.py ================================================ #!/usr/bin/env python3 import os import sys from pathlib import Path def concatenate_language_files(directory, language_ext): """ Find all files ending with '{language_ext}.md', sort them alphabetically, and concatenate them into README-{iso_code}.md """ # Convert to Path object for easier handling dir_path = Path(directory) # Check if directory exists if not dir_path.exists(): print(f"Error: Directory '{directory}' does not exist") return False # Find all files ending with '{language_ext}.md' lang_files = list(dir_path.glob(f'*{language_ext}.md')) if not lang_files: print(f"No files ending with '{language_ext}.md' found in '{directory}'") return False # Sort files alphabetically by filename lang_files.sort(key=lambda x: x.name) # Print sorted list of files print("Files found and sorted alphabetically:") for file in lang_files: print(f" {file.name}") # Use the provided language extension as the ISO code iso_code = language_ext output_file = dir_path / f'README-{iso_code}.md' # Concatenate all files try: with open(output_file, 'w', encoding='utf-8') as outfile: for i, file_path in enumerate(lang_files): print(f"Processing: {file_path.name}") with open(file_path, 'r', encoding='utf-8') as infile: content = infile.read() outfile.write(content) # Add newline between files (except for the last one) if i < len(lang_files) - 1: outfile.write('\n') print(f"\nSuccess! Created '{output_file.name}' with {len(lang_files)} files concatenated.") return True except Exception as e: print(f"Error writing to output file: {e}") return False def main(): # Check for ISO code argument if len(sys.argv) < 2: print("Usage: python concatenate_language_files.py [directory]") print("Example: python concatenate_language_files.py it_IT") print("Example: python concatenate_language_files.py es_ES ../../spa") sys.exit(1) iso_code = sys.argv[1] # Default to ita folder if no directory argument provided if len(sys.argv) > 2: directory = sys.argv[2] else: # From tools/split-md, go up two levels and into ita folder directory = "../../ita" concatenate_language_files(directory, iso_code) if __name__ == "__main__": main() ================================================ FILE: tools/utilities-add-translation/create_language_files.py ================================================ #!/usr/bin/env python3 import os import sys import re from pathlib import Path def split_readme_to_folder(readme_path, target_folder): """Split README.md and save parts directly to target folder""" from split_markdown import split_markdown # Create target folder if it doesn't exist Path(target_folder).mkdir(parents=True, exist_ok=True) # Split README and save parts to target folder split_markdown(readme_path, target_folder) def create_language_files(folder_path, language_ext): folder_path = os.path.abspath(folder_path) if not os.path.exists(folder_path): print(f"Error: Folder '{folder_path}' does not exist") return print(f"Scanning folder: {folder_path}") print(f"Creating files with extension: {language_ext}") for filename in os.listdir(folder_path): match = re.match(r'README-part-(\d+)\.md$', filename) if match: number = match.group(1) lang_filename = f'README-part-{number}-{language_ext}.md' lang_filepath = os.path.join(folder_path, lang_filename) if not os.path.exists(lang_filepath): with open(lang_filepath, 'w', encoding='utf-8') as f: pass # Create empty file print(f'Created: {lang_filename}') if __name__ == "__main__": # Check for language extension argument if len(sys.argv) < 2: print("Usage: python create_language_files.py [folder_path]") print("Example: python create_language_files.py ITA") print("Example: python create_language_files.py FR ../../fra") sys.exit(1) language_ext = sys.argv[1] # Default to ita folder if no folder argument provided if len(sys.argv) > 2: folder_path = sys.argv[2] else: # From tools/split-md, go up two levels and into ita folder folder_path = "../../ita" # First, split README.md into the target folder readme_path = "../../README.md" if os.path.exists(readme_path): print(f"Splitting README.md into {folder_path}...") split_readme_to_folder(readme_path, folder_path) # Then create language files create_language_files(folder_path, language_ext) ================================================ FILE: tools/utilities-add-translation/split_markdown.py ================================================ #!/usr/bin/env python3 import sys import os from pathlib import Path def split_markdown(file_path, output_dir=None, max_chars=3500): """ Splits a markdown file into smaller parts based on a maximum character count. """ with open(file_path, 'r', encoding='utf-8') as f: content = f.read() if len(content) <= max_chars: print(f"File is already under {max_chars} characters. No splitting needed.") return # Determine output directory if output_dir: output_path = Path(output_dir) output_path.mkdir(parents=True, exist_ok=True) base_name = Path(file_path).stem # Get filename without extension base_path = output_path / base_name else: base_path = Path(file_path).with_suffix('') # Remove extension parts = [] current_part = "" lines = content.split('\n') for line in lines: test_part = current_part + ('\n' if current_part else '') + line if len(test_part) <= max_chars: current_part = test_part else: if current_part: parts.append(current_part) current_part = line if current_part: parts.append(current_part) for i, part in enumerate(parts, 1): part_filename = f"{base_path}-part-{i:02d}.md" with open(part_filename, 'w', encoding='utf-8') as f: f.write(part) print(f"Created: {part_filename} ({len(part)} characters)") if __name__ == "__main__": # Parse arguments: file_path [output_directory] if len(sys.argv) > 2: file_path = sys.argv[1] output_dir = sys.argv[2] elif len(sys.argv) > 1: file_path = sys.argv[1] output_dir = None else: # Default: split root README.md and save parts in current directory file_path = "../../README.md" output_dir = None if not os.path.exists(file_path): print(f"Error: File '{file_path}' not found.") sys.exit(1) split_markdown(file_path, output_dir) ================================================ FILE: tools/utilities-add-translation/verify_concatenation.py ================================================ #!/usr/bin/env python3 import os import sys from pathlib import Path def verify_concatenation(directory, language_ext): """ Verify that all language files were properly included in the concatenated file by comparing total character counts """ dir_path = Path(directory) if not dir_path.exists(): print(f"Error: Directory '{directory}' does not exist") return False # Map language extensions to ISO codes lang_map = { 'ITA': 'it_IT', 'ES': 'es_ES', 'FR': 'fr_FR', 'DE': 'de_DE', 'PT': 'pt_PT', 'RU': 'ru_RU', 'JA': 'ja_JP', 'KO': 'ko_KR', 'ZH': 'zh_CN' } iso_code = lang_map.get(language_ext, language_ext.lower()) concatenated_filename = f'README-{iso_code}.md' # Find all files ending with '{language_ext}.md' except the concatenated file lang_files = [f for f in dir_path.glob(f'*{language_ext}.md') if f.name != concatenated_filename] concatenated_file = dir_path / concatenated_filename if not concatenated_file.exists(): print(f"Error: Concatenated file '{concatenated_filename}' not found in '{directory}'") return False if not lang_files: print(f"No {language_ext}.md files found in '{directory}'") return False # Sort files alphabetically lang_files.sort(key=lambda x: x.name) # Count characters in individual files total_chars = 0 file_details = [] print(f"Counting characters in {len(lang_files)} {language_ext}.md files:") for file_path in lang_files: try: with open(file_path, 'r', encoding='utf-8') as f: content = f.read() char_count = len(content) total_chars += char_count file_details.append((file_path.name, char_count)) print(f" {file_path.name}: {char_count:,} characters") except Exception as e: print(f"Error reading {file_path.name}: {e}") return False # Count characters in concatenated file try: with open(concatenated_file, 'r', encoding='utf-8') as f: concatenated_content = f.read() concatenated_chars = len(concatenated_content) except Exception as e: print(f"Error reading concatenated file: {e}") return False # Compare counts print(f"\n--- VERIFICATION RESULTS ---") print(f"Total characters in {len(lang_files)} individual files: {total_chars:,}") print(f"Characters in concatenated file: {concatenated_chars:,}") print(f"Difference: {concatenated_chars - total_chars:,}") if total_chars == concatenated_chars: print("✅ SUCCESS: Character counts match exactly!") print("All files were properly included in the concatenation.") return True else: print("❌ MISMATCH: Character counts do not match!") if concatenated_chars > total_chars: print(f"Concatenated file has {concatenated_chars - total_chars:,} extra characters") else: print(f"Concatenated file is missing {total_chars - concatenated_chars:,} characters") return False def main(): # Check for language extension argument if len(sys.argv) < 2: print("Usage: python verify_concatenation.py [directory]") print("Example: python verify_concatenation.py ITA") print("Example: python verify_concatenation.py ES ../../spa") sys.exit(1) language_ext = sys.argv[1] # Default to ita folder if no directory argument provided if len(sys.argv) > 2: directory = sys.argv[2] else: # From tools/split-md, go up two levels and into ita folder directory = "../../ita" verify_concatenation(directory, language_ext) if __name__ == "__main__": main() ================================================ FILE: tools/utilities-add-translation/verify_concatenation_detailed.py ================================================ #!/usr/bin/env python3 import os import sys from pathlib import Path def verify_concatenation_detailed(directory, language_ext): """ Verify concatenation with detailed analysis including newlines between files """ dir_path = Path(directory) if not dir_path.exists(): print(f"Error: Directory '{directory}' does not exist") return False # Map language extensions to ISO codes lang_map = { 'ITA': 'it_IT', 'ES': 'es_ES', 'FR': 'fr_FR', 'DE': 'de_DE', 'PT': 'pt_PT', 'RU': 'ru_RU', 'JA': 'ja_JP', 'KO': 'ko_KR', 'ZH': 'zh_CN' } iso_code = lang_map.get(language_ext, language_ext.lower()) concatenated_filename = f'README-{iso_code}.md' # Find all files ending with '{language_ext}.md' except the concatenated file lang_files = [f for f in dir_path.glob(f'*{language_ext}.md') if f.name != concatenated_filename] concatenated_file = dir_path / concatenated_filename if not concatenated_file.exists(): print(f"Error: Concatenated file '{concatenated_filename}' not found in '{directory}'") return False if not lang_files: print(f"No {language_ext}.md files found in '{directory}'") return False # Sort files alphabetically lang_files.sort(key=lambda x: x.name) # Count characters in individual files total_chars = 0 print(f"Analyzing {len(lang_files)} {language_ext}.md files:") for file_path in lang_files: try: with open(file_path, 'r', encoding='utf-8') as f: content = f.read() char_count = len(content) total_chars += char_count print(f" {file_path.name}: {char_count:,} characters") except Exception as e: print(f"Error reading {file_path.name}: {e}") return False # Count characters in concatenated file try: with open(concatenated_file, 'r', encoding='utf-8') as f: concatenated_content = f.read() concatenated_chars = len(concatenated_content) except Exception as e: print(f"Error reading concatenated file: {e}") return False # Calculate expected characters with newlines between files # The concatenation script adds newlines between files (n-1 newlines for n files) expected_newlines = len(lang_files) - 1 expected_total = total_chars + expected_newlines # Results print(f"\n--- DETAILED VERIFICATION RESULTS ---") print(f"Individual files: {len(lang_files)} files") print(f"Total characters in individual files: {total_chars:,}") print(f"Expected newlines between files: {expected_newlines}") print(f"Expected total with newlines: {expected_total:,}") print(f"Actual concatenated file characters: {concatenated_chars:,}") print(f"Difference: {concatenated_chars - expected_total:,}") if concatenated_chars == expected_total: print("✅ SUCCESS: Character counts match perfectly!") print("All files were properly concatenated with correct newlines.") return True elif concatenated_chars == total_chars: print("✅ SUCCESS: Character counts match exactly!") print("All files were concatenated without additional newlines.") return True else: print("❌ MISMATCH: Character counts do not match expected values!") if concatenated_chars > expected_total: print(f"Concatenated file has {concatenated_chars - expected_total:,} unexpected extra characters") else: print(f"Concatenated file is missing {expected_total - concatenated_chars:,} characters") # Additional analysis if abs(concatenated_chars - total_chars) < 100: print("Note: Small difference likely due to newline handling between files.") return False def main(): # Check for language extension argument if len(sys.argv) < 2: print("Usage: python verify_concatenation_detailed.py [directory]") print("Example: python verify_concatenation_detailed.py ITA") print("Example: python verify_concatenation_detailed.py ES ../../spa") sys.exit(1) language_ext = sys.argv[1] # Default to ita folder if no directory argument provided if len(sys.argv) > 2: directory = sys.argv[2] else: # From tools/split-md, go up two levels and into ita folder directory = "../../ita" verify_concatenation_detailed(directory, language_ext) if __name__ == "__main__": main() ================================================ FILE: tools/utils.ts ================================================ import { PRETTIER_CONFIG_FILE_PATH } from "./config"; import { Language } from "./i18n"; import * as prettier from 'prettier'; export const makeFilePath = (item: Language) => item === Language.en_EN ? '../README.md' : `../README-${item}.md` export type PrettierOptions = Exclude export const getPrettierOptions = async (): Promise => { const options = await prettier.resolveConfig(PRETTIER_CONFIG_FILE_PATH) if (options === null) { throw `No Prettier options are found! Check your Prettier configuration file.` } return options } ================================================ FILE: tools/verify_codeblocks.py ================================================ import re import sys def verify_codeblocks(filename): """ Verifies that all code blocks in a markdown file have matching opening and closing fences. """ print(f"Verifying {filename}...") try: with open(filename, 'r', encoding='utf-8') as f: lines = f.readlines() except FileNotFoundError: print(f"Error: File {filename} not found.") sys.exit(1) # State in_block = False block_start_line = -1 fence_char = '' fence_length = 0 errors = [] # Regex for fence # Group 1: Leading whitespace # Group 2: Fence characters (```... or ~~~...) # Group 3: Info string / Rest of line fence_pattern = re.compile(r'^(\s*)(`{3,}|~{3,})(.*)$') for i, line in enumerate(lines): lineno = i + 1 # Removing only trailing newline to properly check indentation if needed, # though standard markdown parsers effectively ignore indentation <= 3 spaces for fence? # We will just strip start for the regex match to be simple, but keep strictly to start of line logic if we want to be precise. # Actually proper fence must be at start of line (possibly indented up to 3 spaces). match = fence_pattern.match(line) if match: matched_indent = match.group(1) matched_fence = match.group(2) matched_rest = match.group(3).strip() # If we support indented blocks, we should check len(matched_indent) < 4 # For this repo, assuming standard formatting. current_fence_char = matched_fence[0] current_fence_length = len(matched_fence) if not in_block: # Opening a block in_block = True block_start_line = lineno fence_char = current_fence_char fence_length = current_fence_length # We don't check matched_rest (info string) for opening else: # We are in a block. Check if this is a valid closing fence. # Must be same char # Must be at least as long # Must not have info string (except spaces) if current_fence_char == fence_char and current_fence_length >= fence_length: # It qualifies as a closing fence if rest is empty if matched_rest == "": in_block = False block_start_line = -1 else: # Technically it could be a closing fence with garbage, or just code? # CommonMark: "The closing code fence ... may be followed only by spaces" if matched_rest == "": in_block = False block_start_line = -1 else: # If it has content, it is NOT a closing fence, it's part of the code block. # BUT, if the user intended it to be a close and messed up, we might miss it. # However, strict parser says it's code. pass else: # Different fence inside code block -> treated as content pass if in_block: errors.append(f"Error: Unclosed code block starting at line {block_start_line}") # To help debugging, print the last few lines or context? # User just wants the report. with open("verification_report.md", "w") as report: report.write("# Code Block Verification Report\n\n") report.write(f"File verified: {filename}\n\n") if errors: print("Found issues:") report.write("## Issues Found\n\n") for e in errors: print(e) report.write(f"- {e}\n") sys.exit(1) else: print("Verification passed! No mismatched code blocks found.") report.write("## Status\n\nPASSED: No mismatched code blocks found.\n") sys.exit(0) if __name__ == "__main__": target_file = "README-it_IT.md" if len(sys.argv) > 1: target_file = sys.argv[1] verify_codeblocks(target_file) ================================================ FILE: website/.gitignore ================================================ # build output dist/ # generated types .astro/ # dependencies node_modules/ # logs npm-debug.log* yarn-debug.log* yarn-error.log* pnpm-debug.log* # environment variables .env .env.production # macOS-specific files .DS_Store ================================================ FILE: website/.nvmrc ================================================ v19.7.0 ================================================ FILE: website/.prettierrc ================================================ { "trailingComma": "es5", "tabWidth": 4, "semi": true, "singleQuote": true, "printWidth": 80, "arrowParens": "avoid" } ================================================ FILE: website/.vscode/extensions.json ================================================ { "recommendations": ["astro-build.astro-vscode"], "unwantedRecommendations": [] } ================================================ FILE: website/.vscode/launch.json ================================================ { "version": "0.2.0", "configurations": [ { "command": "./node_modules/.bin/astro dev", "name": "Development server", "request": "launch", "type": "node-terminal" } ] } ================================================ FILE: website/README.md ================================================ # Starlight Starter Kit: Basics [![Built with Starlight](https://astro.badg.es/v2/built-with-starlight/tiny.svg)](https://starlight.astro.build) ``` npm create astro@latest -- --template starlight ``` [![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/withastro/starlight/tree/main/examples/basics) [![Open with CodeSandbox](https://assets.codesandbox.io/github/button-edit-lime.svg)](https://codesandbox.io/p/sandbox/github/withastro/starlight/tree/main/examples/basics) [![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fwithastro%2Fstarlight%2Ftree%2Fmain%2Fexamples%2Fbasics&project-name=my-starlight-docs&repository-name=my-starlight-docs) > 🧑‍🚀 **Seasoned astronaut?** Delete this file. Have fun! ## 🚀 Project Structure Inside of your Astro + Starlight project, you'll see the following folders and files: ``` . ├── public/ ├── src/ │ ├── assets/ │ ├── content/ │ │ ├── docs/ │ │ └── config.ts │ └── env.d.ts ├── astro.config.mjs ├── package.json └── tsconfig.json ``` Starlight looks for `.md` or `.mdx` files in the `src/content/docs/` directory. Each file is exposed as a route based on its file name. Images can be added to `src/assets/` and embedded in Markdown with a relative link. Static assets, like favicons, can be placed in the `public/` directory. ## 🧞 Commands All commands are run from the root of the project, from a terminal: | Command | Action | | :------------------------ | :----------------------------------------------- | | `npm install` | Installs dependencies | | `npm run dev` | Starts local dev server at `localhost:4321` | | `npm run build` | Build your production site to `./dist/` | | `npm run preview` | Preview your build locally, before deploying | | `npm run astro ...` | Run CLI commands like `astro add`, `astro check` | | `npm run astro -- --help` | Get help using the Astro CLI | ## 👀 Want to learn more? Check out [Starlight’s docs](https://starlight.astro.build/), read [the Astro documentation](https://docs.astro.build), or jump into the [Astro Discord server](https://astro.build/chat). ================================================ FILE: website/astro.config.mjs ================================================ import { defineConfig } from 'astro/config'; import starlight from '@astrojs/starlight'; // https://astro.build/config export default defineConfig({ site: 'https://gibbok.github.io', base: '/typescript-book', build: { assets: 'app_assets', }, integrations: [ starlight({ head: [ { tag: 'link', attrs: { rel: 'stylesheet', id: 'silktide-consent-manager-css', href: '/typescript-book/cookie-banner/silktide-consent-manager.css', }, }, { tag: 'script', attrs: { src: '/typescript-book/cookie-banner/silktide-consent-manager.js', }, }, { tag: 'script', content: ` silktideCookieBannerManager.updateCookieBannerConfig({ background: { showBackground: false }, cookieIcon: { position: "bottomRight" }, cookieTypes: [ { id: "analytics", name: "Analytics", description: "

These cookies are necessary for the website to function properly and cannot be switched off. They help with things like logging in and setting your privacy preferences.

", required: false, onAccept: function() { gtag('consent', 'update', { analytics_storage: 'granted', }); dataLayer.push({ 'event': 'consent_accepted_analytics', }); }, onReject: function() { gtag('consent', 'update', { analytics_storage: 'denied', }); } }, { id: "advertising", name: "Advertising", description: "

These cookies help us improve the site by tracking which pages are most popular and how visitors move around the site.

", required: false, onAccept: function() { gtag('consent', 'update', { ad_storage: 'granted', ad_user_data: 'granted', ad_personalization: 'granted', }); dataLayer.push({ 'event': 'consent_accepted_advertising', }); }, onReject: function() { gtag('consent', 'update', { ad_storage: 'denied', ad_user_data: 'denied', ad_personalization: 'denied', }); } } ], text: { banner: { description: "
", acceptAllButtonText: "Accept all", acceptAllButtonAccessibleLabel: "Accept all cookies", rejectNonEssentialButtonText: "Reject non-essential", rejectNonEssentialButtonAccessibleLabel: "Reject non-essential", preferencesButtonText: "Preferences", preferencesButtonAccessibleLabel: "Toggle preferences" }, preferences: { title: "Customize your cookie preferences", description: "

We respect your right to privacy. You can choose not to allow some types of cookies. Your cookie preferences will apply across our website.

", creditLinkText: "Get this banner for free", creditLinkAccessibleLabel: "Get this banner for free" } } }); ` }, { tag: 'script', attrs: { src: 'https://www.googletagmanager.com/gtag/js?id=G-SR2LV8LB90', }, }, { tag: 'script', content: ` window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);} gtag('js', new Date()); gtag('config', 'G-SR2LV8LB90'); ` }, { tag: 'script', attrs: { async: true, src: 'https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-8180626414651391', crossorigin: 'anonymous', }, } ], title: 'TypeScript Book', customCss: ['./src/styles/custom.css'], components: { PageSidebar: './src/components/RightSidebarBanner.astro', Hero: './src/components/Hero.astro', }, social: { github: 'https://github.com/gibbok/typescript-book', 'x.com': 'https://twitter.com/gibbok_coding', }, defaultLocale: 'root', locales: { root: { label: 'English', lang: 'en', }, 'zh-cn': { label: '简体中文', lang: 'zh-CN', }, 'it-it': { label: 'Italiano', lang: 'it-IT', }, 'pt-br': { label: 'Português (Brasil)', lang: 'pt-BR', }, 'sv-se': { label: 'Svenska', lang: 'sv-SE', }, }, sidebar: [ { label: 'TypeScript Book', autogenerate: { directory: 'book' }, }, ], }), ], }); ================================================ FILE: website/package.json ================================================ { "name": "website", "type": "module", "version": "0.0.1", "scripts": { "dev": "astro dev", "start": "astro dev", "build": "astro check && astro build", "preview": "astro preview", "astro": "astro", "deploy": "npm run build && npx gh-pages -d dist" }, "dependencies": { "@astrojs/check": "^0.4.1", "@astrojs/starlight": "^0.15.3", "astro": "^4.0.1", "sharp": "^0.32.5", "typescript": "^5.9.3" }, "devDependencies": { "gh-pages": "^6.1.1" } } ================================================ FILE: website/public/cookie-banner/silktide-consent-manager.css ================================================ /* Silktide Consent Manager - https://silktide.com/consent-manager/ Styles are at risked of being overridden by styles coming from the site the consent manager is used on. To help prevent this, global wrapper elements are prefixed with "#silktide-" */ /* -------------------------------- Global Styles - These elements exist in the main DOM and styling is limited to positioning and animation -------------------------------- */ /* Wrapper (Global) */ #silktide-wrapper { --focus: 0 0 0 2px #ffffff, 0 0 0 4px #000000, 0 0 0 6px #ffffff; --boxShadow: -5px 5px 10px 0px #00000012, 0px 0px 50px 0px #0000001a; --fontFamily: Helvetica Neue, Segoe UI, Arial, sans-serif; --primaryColor: #533BE2; --backgroundColor: #FFFFFF; --textColor: #4B494B; --backdropBackgroundColor: #00000033; --backdropBackgroundBlur: 0px; --cookieIconColor: #533BE2; --cookieIconBackgroundColor: #FFFFFF; position: fixed; bottom: 0; right: 0; width: 100%; height: 100%; z-index: 99999; pointer-events: none; border: 0px; display: flex; justify-content: center; align-items: center } /* Backdrop (Global) */ #silktide-backdrop-global { position: fixed; top: 0; left: 0; width: 100%; height: 100%; pointer-events: auto; border: 0px; display: none; } /* -------------------------------- Links -------------------------------- */ #silktide-wrapper a { all: unset; display: inline-block; color: var(--primaryColor); text-decoration: underline; } #silktide-wrapper a:hover { cursor: pointer; color: var(--textColor); } /* -------------------------------- Focus Styles -------------------------------- */ #silktide-wrapper a:focus, #silktide-wrapper #silktide-banner button:focus, #silktide-wrapper #silktide-modal button:focus, #silktide-wrapper #silktide-cookie-icon:focus { outline: none; box-shadow: var(--focus); border-radius: 5px; } #silktide-wrapper #silktide-cookie-icon:focus { border-radius: 50%; } /* -------------------------------- General Styles -------------------------------- */ #silktide-wrapper .st-button { color: var(--backgroundColor); background-color: var(--primaryColor); border: 2px solid var(--primaryColor); padding: 10px 20px; text-decoration: none; text-align: center; display: inline-block; font-size: 16px; line-height: 24px; cursor: pointer; border-radius: 5px; } #silktide-wrapper .st-button--primary { } #silktide-wrapper .st-button--primary:hover { background-color: var(--backgroundColor); color: var(--primaryColor); } #silktide-wrapper .st-button--secondary { background-color: var(--backgroundColor); color: var(--primaryColor); } #silktide-wrapper .st-button--secondary:hover { background-color: var(--primaryColor); color: var(--backgroundColor); } /* -------------------------------- Banner -------------------------------- */ #silktide-banner { font-family: var(--fontFamily); color: var(--textColor); background-color: var(--backgroundColor); box-sizing: border-box; padding: 32px; border-radius: 5px; pointer-events: auto; border: 0px; position: fixed; bottom: 16px; right: 16px; width: 600px; overflow: auto; max-width: calc(100% - 32px); max-height: calc(100vh - 32px); transform: translate(0, -20px); opacity: 0; animation: silktide-slideInDown 350ms ease-out forwards; animation-delay: 0.3s; box-shadow: -5px 5px 10px 0px #00000012, 0px 0px 50px 0px #0000001a; } #silktide-banner:focus { border-radius: 50%; } #silktide-banner.center { top: 50%; left: 50%; bottom: auto; right: auto; position: fixed; transform: translate(-50%, calc(-50% - 20px)); animation: silktide-slideInDown-center 350ms ease-out forwards; } #silktide-banner.bottomLeft { bottom: 16px; left: 16px; position: fixed; } #silktide-banner.bottomCenter { bottom: 16px; left: 50%; position: fixed; transform: translate(-50%, -20px); animation: silktide-slideInDown-bottomCenter 350ms ease-out forwards; } #silktide-banner .preferences { display: flex; gap: 5px; border: none; padding: 15px 0px; background-color: transparent; color: var(--primaryColor); cursor: pointer; font-size: 16px; } #silktide-banner .preferences span { display: block; white-space: nowrap; text-decoration: underline; } #silktide-banner .preferences span:hover { color: var(--textColor); } #silktide-banner .preferences:after { display: block; content: '>'; text-decoration: none; } #silktide-banner p { font-size: 16px; line-height: 24px; margin: 0px 0px 15px; } #silktide-banner a { display: inline-block; color: var(--primaryColor); text-decoration: underline; background-color: var(--backgroundColor); } #silktide-banner a:hover { color: var(--textColor); } #silktide-banner a.silktide-logo { display: flex; align-items: center; justify-content: center; fill: var(--primaryColor); /* passed down to svg > path */ margin-left: auto; width: 48px; height: 48px; } #silktide-banner .actions { display: flex; gap: 16px; flex-direction: column; margin-top: 24px; } @media (min-width: 600px) { #silktide-banner .actions { flex-direction: row; align-items: center; } } #silktide-banner .actions-row { display: flex; gap: 16px; flex-direction: row; align-items: center; justify-content: space-between; flex-grow: 1; } /* -------------------------------- Modal -------------------------------- */ #silktide-modal { display: none; pointer-events: auto; overflow: auto; width: 800px; max-width: 100%; max-height: 100%; border: 0px; transform: translate(0px, -20px); opacity: 0; animation: silktide-slideInUp-center 350ms ease-out forwards; box-shadow: -5px 5px 10px 0px #00000012, 0px 0px 50px 0px #0000001a; font-family: var(--fontFamily); color: var(--textColor); flex-direction: column; padding: 30px; background-color: var(--backgroundColor); border-radius: 5px; box-sizing: border-box; } /* -------------------------------- Modal - Header -------------------------------- */ #silktide-modal header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; gap: 16px; } #silktide-modal h1 { font-family: var(--fontFamily); color: var(--textColor); font-size: 24px; font-weight: 500; margin: 0px; } #silktide-modal .modal-close { display: inline-flex; border: none; padding: 13px; border: 0px; cursor: pointer; background: var(--backgroundColor); color: var(--primaryColor); } #silktide-modal .modal-close svg { fill: var(--primaryColor); } /* -------------------------------- Modal - Content -------------------------------- */ #silktide-modal section { flex: 1; margin-top: 32px; } #silktide-modal section::-webkit-scrollbar { display: block; /* Force scrollbars to show */ width: 5px; /* Width of the scrollbar */ } #silktide-modal section::-webkit-scrollbar-thumb { background-color: var(--textColor); /* Color of the scrollbar thumb */ border-radius: 10px; /* Rounded corners for the thumb */ } #silktide-modal p { font-size: 16px; line-height: 24px; color: var(--textColor); margin: 0px 0px 15px; } #silktide-modal p:last-of-type { margin: 0px; } #silktide-modal fieldset { padding: 0px; border: none; margin: 0px 0px 32px; } #silktide-modal fieldset:last-of-type { margin: 0px; } #silktide-modal legend { padding: 0px; margin: 0px 0px 10px; font-weight: 700; color: var(--textColor); font-size: 16px; } #silktide-modal .cookie-type-content { display: flex; justify-content: space-between; align-items: flex-start; gap: 24px; } /* -------------------------------- Modal - Switches -------------------------------- */ #silktide-modal .switch { flex-shrink: 0; position: relative; display: inline-block; height: 34px; width: 74px; cursor: pointer; } #silktide-modal .switch:focus-within { outline: none; box-shadow: var(--focus); border-radius: 25px; } #silktide-modal .switch input { opacity: 0; position: absolute; } /* Unchecked Switch Styles */ #silktide-modal .switch__pill { position: relative; display: block; height: 34px; width: 74px; background: var(--textColor); border-radius: 25px; } #silktide-modal .switch__dot { position: absolute; top: 2px; left: 2px; display: block; height: 30px; width: 30px; background: var(--backgroundColor); border-radius: 50%; transition: left 150ms ease-out; } #silktide-modal .switch__off, #silktide-modal .switch__on { text-transform: uppercase; font-size: 15px; font-weight: 500; color: var(--backgroundColor); position: absolute; top: 7px; right: 8px; transition: right 150ms ease-out, opacity 150ms ease-out; } #silktide-modal .switch__off { opacity: 1; } #silktide-modal .switch__on { opacity: 0; } /* Checked Switch Styles */ #silktide-modal .switch input:checked + .switch__pill { background: var(--primaryColor); } #silktide-modal .switch input:checked ~ .switch__dot { left: calc(100% - 32px); } #silktide-modal .switch input:checked ~ .switch__off { right: calc(100% - 32px); opacity: 0; } #silktide-modal .switch input:checked ~ .switch__on { right: calc(100% - 34px); opacity: 1; } /* Disabled Switch Styles */ #silktide-modal .switch input:disabled + .switch__pill { opacity: 0.65; cursor: not-allowed; } /* -------------------------------- Modal - Footer -------------------------------- */ #silktide-modal footer { display: flex; flex-direction: column; gap: 16px; margin-top: 24px; } @media (min-width: 600px) { #silktide-modal footer { flex-direction: row; align-items: center; } } #silktide-modal footer a { margin-left: auto; padding: 14px 0px; } /* Cookie Icon */ #silktide-cookie-icon { display: none; position: fixed; bottom: 10px; left: 10px; justify-content: center; align-items: center; width: 60px; height: 60px; border-radius: 50%; padding: 0px; border: none; background-color: var(--cookieIconColor); cursor: pointer; box-shadow: 0px 0px 6px 0px #0000001a; pointer-events: auto; animation: silktide-fadeIn 0.3s ease-in-out forwards; transform: scale(0.7); } #silktide-cookie-icon.bottomRight { left: auto; right: 10px; } #silktide-cookie-icon svg { fill: var(--cookieIconBackgroundColor); } /* -------------------------------- Backdrop -------------------------------- */ #silktide-backdrop { display: none; position: absolute; top: 0; left: 0; width: 100%; height: 100%; background-color: var(--backdropBackgroundColor); backdrop-filter: blur(var(--backdropBackgroundBlur)); pointer-events: all; } /* -------------------------------- Animations -------------------------------- */ @keyframes silktide-fadeIn { from { opacity: 0; } to { opacity: 1; } } @keyframes silktide-slideInDown { from { opacity: 0; transform: translateY(-20px); } to { opacity: 1; transform: translateY(0); } } @keyframes silktide-slideInDown-center { from { opacity: 0; transform: translate(-50%, calc(-50% - 20px)); } to { opacity: 1; transform: translate(-50%, -50%); } } @keyframes silktide-slideInDown-bottomCenter { from { opacity: 0; transform: translate(-50%, -20px); } to { opacity: 1; transform: translate(-50%, 0); } } @keyframes silktide-slideInUp-center { from { opacity: 0; transform: translate(0px, 20px); } to { opacity: 1; transform: translate(0px, 0px); } } ================================================ FILE: website/public/cookie-banner/silktide-consent-manager.js ================================================ // Silktide Consent Manager - https://silktide.com/consent-manager/ class SilktideCookieBanner { constructor(config) { this.config = config; // Save config to the instance this.wrapper = null; this.banner = null; this.modal = null; this.cookieIcon = null; this.backdrop = null; this.createWrapper(); if (this.shouldShowBackdrop()) { this.createBackdrop(); } this.createCookieIcon(); this.createModal(); if (this.shouldShowBanner()) { this.createBanner(); this.showBackdrop(); } else { this.showCookieIcon(); } this.setupEventListeners(); if (this.hasSetInitialCookieChoices()) { this.loadRequiredCookies(); this.runAcceptedCookieCallbacks(); } } destroyCookieBanner() { // Remove all cookie banner elements from the DOM if (this.wrapper && this.wrapper.parentNode) { this.wrapper.parentNode.removeChild(this.wrapper); } // Restore scrolling this.allowBodyScroll(); // Clear all references this.wrapper = null; this.banner = null; this.modal = null; this.cookieIcon = null; this.backdrop = null; } // ---------------------------------------------------------------- // Wrapper // ---------------------------------------------------------------- createWrapper() { this.wrapper = document.createElement('div'); this.wrapper.id = 'silktide-wrapper'; document.body.insertBefore(this.wrapper, document.body.firstChild); } // ---------------------------------------------------------------- // Wrapper Child Generator // ---------------------------------------------------------------- createWrapperChild(htmlContent, id) { // Create child element const child = document.createElement('div'); child.id = id; child.innerHTML = htmlContent; // Ensure wrapper exists if (!this.wrapper || !document.body.contains(this.wrapper)) { this.createWrapper(); } // Append child to wrapper this.wrapper.appendChild(child); return child; } // ---------------------------------------------------------------- // Backdrop // ---------------------------------------------------------------- createBackdrop() { this.backdrop = this.createWrapperChild(null, 'silktide-backdrop'); } showBackdrop() { if (this.backdrop) { this.backdrop.style.display = 'block'; } // Trigger optional onBackdropOpen callback if (typeof this.config.onBackdropOpen === 'function') { this.config.onBackdropOpen(); } } hideBackdrop() { if (this.backdrop) { this.backdrop.style.display = 'none'; } // Trigger optional onBackdropClose callback if (typeof this.config.onBackdropClose === 'function') { this.config.onBackdropClose(); } } shouldShowBackdrop() { return this.config?.background?.showBackground || false; } // update the checkboxes in the modal with the values from localStorage updateCheckboxState(saveToStorage = false) { const preferencesSection = this.modal.querySelector('#cookie-preferences'); const checkboxes = preferencesSection.querySelectorAll('input[type="checkbox"]'); checkboxes.forEach((checkbox) => { const [, cookieId] = checkbox.id.split('cookies-'); const cookieType = this.config.cookieTypes.find(type => type.id === cookieId); if (!cookieType) return; if (saveToStorage) { // Save the current state to localStorage and run callbacks const currentState = checkbox.checked; if (cookieType.required) { localStorage.setItem( `silktideCookieChoice_${cookieId}${this.getBannerSuffix()}`, 'true' ); } else { localStorage.setItem( `silktideCookieChoice_${cookieId}${this.getBannerSuffix()}`, currentState.toString() ); // Run appropriate callback if (currentState && typeof cookieType.onAccept === 'function') { cookieType.onAccept(); } else if (!currentState && typeof cookieType.onReject === 'function') { cookieType.onReject(); } } } else { // When reading values (opening modal) if (cookieType.required) { checkbox.checked = true; checkbox.disabled = true; } else { const storedValue = localStorage.getItem( `silktideCookieChoice_${cookieId}${this.getBannerSuffix()}` ); if (storedValue !== null) { checkbox.checked = storedValue === 'true'; } else { checkbox.checked = !!cookieType.defaultValue; } } } }); } setInitialCookieChoiceMade() { window.localStorage.setItem(`silktideCookieBanner_InitialChoice${this.getBannerSuffix()}`, 1); } // ---------------------------------------------------------------- // Consent Handling // ---------------------------------------------------------------- handleCookieChoice(accepted) { // We set that an initial choice was made regardless of what it was so we don't show the banner again this.setInitialCookieChoiceMade(); this.removeBanner(); this.hideBackdrop(); this.toggleModal(false); this.showCookieIcon(); this.config.cookieTypes.forEach((type) => { // Set localStorage and run accept/reject callbacks if (type.required == true) { localStorage.setItem(`silktideCookieChoice_${type.id}${this.getBannerSuffix()}`, 'true'); if (typeof type.onAccept === 'function') { type.onAccept() } } else { localStorage.setItem( `silktideCookieChoice_${type.id}${this.getBannerSuffix()}`, accepted.toString(), ); if (accepted) { if (typeof type.onAccept === 'function') { type.onAccept(); } } else { if (typeof type.onReject === 'function') { type.onReject(); } } } }); // Trigger optional onAcceptAll/onRejectAll callbacks if (accepted && typeof this.config.onAcceptAll === 'function') { if (typeof this.config.onAcceptAll === 'function') { this.config.onAcceptAll(); } } else if (typeof this.config.onRejectAll === 'function') { if (typeof this.config.onRejectAll === 'function') { this.config.onRejectAll(); } } // finally update the checkboxes in the modal with the values from localStorage this.updateCheckboxState(); } getAcceptedCookies() { return (this.config.cookieTypes || []).reduce((acc, cookieType) => { acc[cookieType.id] = localStorage.getItem(`silktideCookieChoice_${cookieType.id}${this.getBannerSuffix()}`) === 'true'; return acc; }, {}); } runAcceptedCookieCallbacks() { if (!this.config.cookieTypes) return; const acceptedCookies = this.getAcceptedCookies(); this.config.cookieTypes.forEach((type) => { if (type.required) return; // we run required cookies separately in loadRequiredCookies if (acceptedCookies[type.id] && typeof type.onAccept === 'function') { if (typeof type.onAccept === 'function') { type.onAccept(); } } }); } runRejectedCookieCallbacks() { if (!this.config.cookieTypes) return; const rejectedCookies = this.getRejectedCookies(); this.config.cookieTypes.forEach((type) => { if (rejectedCookies[type.id] && typeof type.onReject === 'function') { if (typeof type.onReject === 'function') { type.onReject(); } } }); } /** * Run through all of the cookie callbacks based on the current localStorage values */ runStoredCookiePreferenceCallbacks() { this.config.cookieTypes.forEach((type) => { const accepted = localStorage.getItem(`silktideCookieChoice_${type.id}${this.getBannerSuffix()}`) === 'true'; // Set localStorage and run accept/reject callbacks if (accepted) { if (typeof type.onAccept === 'function') { type.onAccept(); } } else { if (typeof type.onReject === 'function') { type.onReject(); } } }); } loadRequiredCookies() { if (!this.config.cookieTypes) return; this.config.cookieTypes.forEach((cookie) => { if (cookie.required && typeof cookie.onAccept === 'function') { if (typeof cookie.onAccept === 'function') { cookie.onAccept(); } } }); } // ---------------------------------------------------------------- // Banner // ---------------------------------------------------------------- getBannerContent() { const bannerDescription = this.config.text?.banner?.description || "

We use cookies on our site to enhance your user experience, provide personalized content, and analyze our traffic.

"; // Accept button const acceptAllButtonText = this.config.text?.banner?.acceptAllButtonText || 'Accept all'; const acceptAllButtonLabel = this.config.text?.banner?.acceptAllButtonAccessibleLabel; const acceptAllButton = ``; // Reject button const rejectNonEssentialButtonText = this.config.text?.banner?.rejectNonEssentialButtonText || 'Reject non-essential'; const rejectNonEssentialButtonLabel = this.config.text?.banner?.rejectNonEssentialButtonAccessibleLabel; const rejectNonEssentialButton = ``; // Preferences button const preferencesButtonText = this.config.text?.banner?.preferencesButtonText || 'Preferences'; const preferencesButtonLabel = this.config.text?.banner?.preferencesButtonAccessibleLabel; const preferencesButton = ``; // Silktide logo link const silktideLogo = ` `; const bannerContent = ` ${bannerDescription}
${acceptAllButton} ${rejectNonEssentialButton}
${preferencesButton} ${silktideLogo}
`; return bannerContent; } hasSetInitialCookieChoices() { return !!localStorage.getItem(`silktideCookieBanner_InitialChoice${this.getBannerSuffix()}`); } createBanner() { // Create banner element this.banner = this.createWrapperChild(this.getBannerContent(), 'silktide-banner'); // Add positioning class from config if (this.banner && this.config.position?.banner) { this.banner.classList.add(this.config.position.banner); } // Trigger optional onBannerOpen callback if (this.banner && typeof this.config.onBannerOpen === 'function') { this.config.onBannerOpen(); } } removeBanner() { if (this.banner && this.banner.parentNode) { this.banner.parentNode.removeChild(this.banner); this.banner = null; // Trigger optional onBannerClose callback if (typeof this.config.onBannerClose === 'function') { this.config.onBannerClose(); } } } shouldShowBanner() { if (this.config.showBanner === false) { return false; } return ( localStorage.getItem(`silktideCookieBanner_InitialChoice${this.getBannerSuffix()}`) === null ); } // ---------------------------------------------------------------- // Modal // ---------------------------------------------------------------- getModalContent() { const preferencesTitle = this.config.text?.preferences?.title || 'Customize your cookie preferences'; const preferencesDescription = this.config.text?.preferences?.description || "

We respect your right to privacy. You can choose not to allow some types of cookies. Your cookie preferences will apply across our website.

"; // Preferences button const preferencesButtonLabel = this.config.text?.banner?.preferencesButtonAccessibleLabel; const closeModalButton = ``; const cookieTypes = this.config.cookieTypes || []; const acceptedCookieMap = this.getAcceptedCookies(); // Accept button const acceptAllButtonText = this.config.text?.banner?.acceptAllButtonText || 'Accept all'; const acceptAllButtonLabel = this.config.text?.banner?.acceptAllButtonAccessibleLabel; const acceptAllButton = ``; // Reject button const rejectNonEssentialButtonText = this.config.text?.banner?.rejectNonEssentialButtonText || 'Reject non-essential'; const rejectNonEssentialButtonLabel = this.config.text?.banner?.rejectNonEssentialButtonAccessibleLabel; const rejectNonEssentialButton = ``; // Credit link const creditLinkText = this.config.text?.preferences?.creditLinkText || 'Get this banner for free'; const creditLinkAccessibleLabel = this.config.text?.preferences?.creditLinkAccessibleLabel; const creditLink = `${creditLinkText}`; const modalContent = `

${preferencesTitle}

${closeModalButton}
${preferencesDescription}
${acceptAllButton} ${rejectNonEssentialButton} ${creditLink}
`; return modalContent; } createModal() { // Create banner element this.modal = this.createWrapperChild(this.getModalContent(), 'silktide-modal'); } toggleModal(show) { if (!this.modal) return; this.modal.style.display = show ? 'flex' : 'none'; if (show) { this.showBackdrop(); this.hideCookieIcon(); this.removeBanner(); this.preventBodyScroll(); // Focus the close button const modalCloseButton = this.modal.querySelector('.modal-close'); modalCloseButton.focus(); // Trigger optional onPreferencesOpen callback if (typeof this.config.onPreferencesOpen === 'function') { this.config.onPreferencesOpen(); } this.updateCheckboxState(false); // read from storage when opening } else { // Set that an initial choice was made when closing the modal this.setInitialCookieChoiceMade(); // Save current checkbox states to storage this.updateCheckboxState(true); this.hideBackdrop(); this.showCookieIcon(); this.allowBodyScroll(); // Trigger optional onPreferencesClose callback if (typeof this.config.onPreferencesClose === 'function') { this.config.onPreferencesClose(); } } } // ---------------------------------------------------------------- // Cookie Icon // ---------------------------------------------------------------- getCookieIconContent() { return ` `; } createCookieIcon() { this.cookieIcon = document.createElement('button'); this.cookieIcon.id = 'silktide-cookie-icon'; this.cookieIcon.title = 'Manage your cookie preferences for this site'; this.cookieIcon.innerHTML = this.getCookieIconContent(); if (this.config.text?.banner?.preferencesButtonAccessibleLabel) { this.cookieIcon.ariaLabel = this.config.text?.banner?.preferencesButtonAccessibleLabel; } // Ensure wrapper exists if (!this.wrapper || !document.body.contains(this.wrapper)) { this.createWrapper(); } // Append child to wrapper this.wrapper.appendChild(this.cookieIcon); // Add positioning class from config if (this.cookieIcon && this.config.cookieIcon?.position) { this.cookieIcon.classList.add(this.config.cookieIcon.position); } // Add color scheme class from config if (this.cookieIcon && this.config.cookieIcon?.colorScheme) { this.cookieIcon.classList.add(this.config.cookieIcon.colorScheme); } } showCookieIcon() { if (this.cookieIcon) { this.cookieIcon.style.display = 'flex'; } } hideCookieIcon() { if (this.cookieIcon) { this.cookieIcon.style.display = 'none'; } } /** * This runs if the user closes the modal without making a choice for the first time * We apply the default values and the necessary values as default */ handleClosedWithNoChoice() { this.config.cookieTypes.forEach((type) => { let accepted = true; // Set localStorage and run accept/reject callbacks if (type.required == true || type.defaultValue) { localStorage.setItem( `silktideCookieChoice_${type.id}${this.getBannerSuffix()}`, accepted.toString(), ); } else { accepted = false; localStorage.setItem( `silktideCookieChoice_${type.id}${this.getBannerSuffix()}`, accepted.toString(), ); } if (accepted) { if (typeof type.onAccept === 'function') { type.onAccept(); } } else { if (typeof type.onReject === 'function') { type.onReject(); } } // set the flag to say that the cookie choice has been made this.setInitialCookieChoiceMade(); this.updateCheckboxState(); }); } // ---------------------------------------------------------------- // Focusable Elements // ---------------------------------------------------------------- getFocusableElements(element) { return element.querySelectorAll( 'button, a[href], input, select, textarea, [tabindex]:not([tabindex="-1"])', ); } // ---------------------------------------------------------------- // Event Listeners // ---------------------------------------------------------------- setupEventListeners() { // Check Banner exists before trying to add event listeners if (this.banner) { // Get the buttons const acceptButton = this.banner.querySelector('.accept-all'); const rejectButton = this.banner.querySelector('.reject-all'); const preferencesButton = this.banner.querySelector('.preferences'); // Add event listeners to the buttons acceptButton?.addEventListener('click', () => this.handleCookieChoice(true)); rejectButton?.addEventListener('click', () => this.handleCookieChoice(false)); preferencesButton?.addEventListener('click', () => { this.showBackdrop(); this.toggleModal(true); }); // Focus Trap const focusableElements = this.getFocusableElements(this.banner); const firstFocusableEl = focusableElements[0]; const lastFocusableEl = focusableElements[focusableElements.length - 1]; // Add keydown event listener to handle tab navigation this.banner.addEventListener('keydown', (e) => { if (e.key === 'Tab') { if (e.shiftKey) { if (document.activeElement === firstFocusableEl) { lastFocusableEl.focus(); e.preventDefault(); } } else { if (document.activeElement === lastFocusableEl) { firstFocusableEl.focus(); e.preventDefault(); } } } }); // Set initial focus if (this.config.mode !== 'wizard') { acceptButton?.focus(); } } // Check Modal exists before trying to add event listeners if (this.modal) { const closeButton = this.modal.querySelector('.modal-close'); const acceptAllButton = this.modal.querySelector('.preferences-accept-all'); const rejectAllButton = this.modal.querySelector('.preferences-reject-all'); closeButton?.addEventListener('click', () => { this.toggleModal(false); const hasMadeFirstChoice = this.hasSetInitialCookieChoices(); if (hasMadeFirstChoice) { // run through the callbacks based on the current localStorage state this.runStoredCookiePreferenceCallbacks(); } else { // handle the case where the user closes without making a choice for the first time this.handleClosedWithNoChoice(); } }); acceptAllButton?.addEventListener('click', () => this.handleCookieChoice(true)); rejectAllButton?.addEventListener('click', () => this.handleCookieChoice(false)); // Banner Focus Trap const focusableElements = this.getFocusableElements(this.modal); const firstFocusableEl = focusableElements[0]; const lastFocusableEl = focusableElements[focusableElements.length - 1]; this.modal.addEventListener('keydown', (e) => { if (e.key === 'Tab') { if (e.shiftKey) { if (document.activeElement === firstFocusableEl) { lastFocusableEl.focus(); e.preventDefault(); } } else { if (document.activeElement === lastFocusableEl) { firstFocusableEl.focus(); e.preventDefault(); } } } if (e.key === 'Escape') { this.toggleModal(false); } }); closeButton?.focus(); // Update the checkbox event listeners const preferencesSection = this.modal.querySelector('#cookie-preferences'); const checkboxes = preferencesSection.querySelectorAll('input[type="checkbox"]'); checkboxes.forEach(checkbox => { checkbox.addEventListener('change', (event) => { const [, cookieId] = event.target.id.split('cookies-'); const isAccepted = event.target.checked; const previousValue = localStorage.getItem( `silktideCookieChoice_${cookieId}${this.getBannerSuffix()}` ) === 'true'; // Only proceed if the value has actually changed if (isAccepted !== previousValue) { // Find the corresponding cookie type const cookieType = this.config.cookieTypes.find(type => type.id === cookieId); if (cookieType) { // Update localStorage localStorage.setItem( `silktideCookieChoice_${cookieId}${this.getBannerSuffix()}`, isAccepted.toString() ); // Run the appropriate callback only if the value changed if (isAccepted && typeof cookieType.onAccept === 'function') { cookieType.onAccept(); } else if (!isAccepted && typeof cookieType.onReject === 'function') { cookieType.onReject(); } } } }); }); } // Check Cookie Icon exists before trying to add event listeners if (this.cookieIcon) { this.cookieIcon.addEventListener('click', () => { // If modal is not found, create it if (!this.modal) { this.createModal(); this.toggleModal(true); this.hideCookieIcon(); } // If modal is hidden, show it else if (this.modal.style.display === 'none' || this.modal.style.display === '') { this.toggleModal(true); this.hideCookieIcon(); } // If modal is visible, hide it else { this.toggleModal(false); } }); } } getBannerSuffix() { if (this.config.bannerSuffix) { return '_' + this.config.bannerSuffix; } return ''; } preventBodyScroll() { document.body.style.overflow = 'hidden'; // Prevent iOS Safari scrolling document.body.style.position = 'fixed'; document.body.style.width = '100%'; } allowBodyScroll() { document.body.style.overflow = ''; document.body.style.position = ''; document.body.style.width = ''; } } (function () { window.silktideCookieBannerManager = {}; let config = {}; let cookieBanner; function updateCookieBannerConfig(userConfig = {}) { config = {...config, ...userConfig}; // If cookie banner exists, destroy and recreate it with new config if (cookieBanner) { cookieBanner.destroyCookieBanner(); // We'll need to add this method cookieBanner = null; } // Only initialize if document.body exists if (document.body) { initCookieBanner(); } else { // Wait for DOM to be ready document.addEventListener('DOMContentLoaded', initCookieBanner, {once: true}); } } function initCookieBanner() { if (!cookieBanner) { cookieBanner = new SilktideCookieBanner(config); // Pass config to the CookieBanner instance } } function injectScript(url, loadOption) { // Check if script with this URL already exists const existingScript = document.querySelector(`script[src="${url}"]`); if (existingScript) { return; // Script already exists, don't add it again } const script = document.createElement('script'); script.src = url; // Apply the async or defer attribute based on the loadOption parameter if (loadOption === 'async') { script.async = true; } else if (loadOption === 'defer') { script.defer = true; } document.head.appendChild(script); } window.silktideCookieBannerManager.initCookieBanner = initCookieBanner; window.silktideCookieBannerManager.updateCookieBannerConfig = updateCookieBannerConfig; window.silktideCookieBannerManager.injectScript = injectScript; if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initCookieBanner, {once: true}); } else { initCookieBanner(); } })(); ================================================ FILE: website/public/cookie_policy.html ================================================ Cookie Policy

Cookie Policy

This Cookie Policy explains how we use cookies and similar technologies to recognize you when you visit our website. It explains what these technologies are and why we use them, as well as your rights to control our use of them.

What are cookies?

Cookies are small data files that are placed on your computer or mobile device when you visit a website. Cookies are widely used by website owners in order to make their websites work, or to work more efficiently, as well as to provide reporting information.

Types of Cookies We Use

Necessary cookies

These cookies are strictly necessary to provide you with services available through our website and to use some of its features. Because these cookies are essential for the website to function properly, you cannot refuse them without impacting how our site functions. They help with basic functionalities such as secure login, setting your privacy preferences, and maintaining session state.

Analytical Cookies (e.g., Google Analytics)

Analytical cookies collect information that is used either in aggregate form to help us understand how our website is being used or how effective our marketing campaigns are, or to help us customize our website for you. They help us collect reporting information on its usage and continuously optimize the user experience.

Marketing/Advertising Cookies (e.g., Google AdSense or Carbon Ads)

We use marketing cookies to make advertising messages more relevant to you. They perform functions like preventing the same ad from continuously reappearing, ensuring that ads are properly displayed for advertisers, and in some cases selecting advertisements that are based on your specific interests. These cookies are typically provided by third-party advertising networks.

How can you control cookies?

You have the right to decide whether to accept or reject non-essential cookies. You can exercise your cookie rights by setting your preferences in the Cookie Consent Manager or by amending your web browser controls to accept or refuse cookies. If you choose to reject cookies, you may still use our website though your access to some functionality and areas of our website may be restricted.

================================================ FILE: website/public/google184c8848cff38265.html ================================================ google-site-verification: google184c8848cff38265.html ================================================ FILE: website/src/components/Hero.astro ================================================ --- import { Image } from 'astro:assets'; import CallToAction from '@astrojs/starlight/components/CallToAction.astro'; import type { Props } from '@astrojs/starlight/props'; const { entry } = Astro.props; const { data } = entry; const { title = data.title, tagline, image, actions = [] } = data.hero || {}; const imageAttrs = { loading: 'eager' as const, decoding: 'async' as const, width: 400, height: 400, alt: image?.alt || '', }; let darkImage: ImageMetadata | undefined; let lightImage: ImageMetadata | undefined; let rawHtml: string | undefined; if (image) { if ('file' in image) { darkImage = image.file; } else if ('dark' in image) { darkImage = image.dark; lightImage = image.light; } else { rawHtml = image.html; } } ---
{darkImage && ( )} {lightImage && } {rawHtml &&
}

{tagline &&
}
{actions.length > 0 && (
{actions.map((action) => ( {action.text} ))}
)}

================================================ FILE: website/src/components/RightSidebarBanner.astro ================================================ --- import type { Props } from '@astrojs/starlight/props'; import Default from '@astrojs/starlight/components/PageSidebar.astro'; const props = Astro.props as Props; --- ================================================ FILE: website/src/content/config.ts ================================================ import { defineCollection } from 'astro:content'; import { docsSchema, i18nSchema } from '@astrojs/starlight/schema'; export const collections = { docs: defineCollection({ schema: docsSchema() }), i18n: defineCollection({ type: 'data', schema: i18nSchema() }), }; ================================================ FILE: website/src/content/docs/book/about-the-author.md ================================================ --- title: About the Author sidebar: order: 6 label: 6. About the Author --- Simone Poggiali is an experienced Staff Engineer with a passion for writing professional-grade code since the 90s. Throughout his international career, he has contributed to numerous projects for a wide range of clients, from startups to large organizations. Notable companies such as HelloFresh, Siemens, O2, Leroy Merlin and Snowplow have benefited from his expertise and dedication. You can reach Simone Poggiali on the following platforms: * LinkedIn: [https://www.linkedin.com/in/simone-poggiali](https://www.linkedin.com/in/simone-poggiali) * GitHub: [https://github.com/gibbok](https://github.com/gibbok) * X.com: [https://x.com/gibbok_coding](https://x.com/gibbok_coding) * Email: gibbok.coding📧gmail.com Full list of contributors: [https://github.com/gibbok/typescript-book/graphs/contributors](https://github.com/gibbok/typescript-book/graphs/contributors) ================================================ FILE: website/src/content/docs/book/any-type.md ================================================ --- title: Any type sidebar: order: 44 label: 44. Any type --- The `any` type is a special type (universal supertype) that can be used to represent any type of value (primitives, objects, arrays, functions, errors, symbols). It is often used in situations where the type of a value is not known at compile time, or when working with values from external APIs or libraries that do not have TypeScript typings. By utilizing `any` type, you are indicating to the TypeScript compiler that values should be represented without any limitations. To maximize type safety in your code, consider the following: * Limit the usage of `any` to specific cases where the type is truly unknown. * Do not return `any` types from a function, as this weakens type safety in code that uses it. * Instead of `any` use `@ts-ignore` if you need to silence the compiler. ```typescript let value: any; value = true; // Valid value = 7; // Valid ``` ================================================ FILE: website/src/content/docs/book/assignments.md ================================================ --- title: Assignments sidebar: order: 21 label: 21. Assignments --- TypeScript narrowing using assignments is a way to narrow the type of a variable based on the value assigned to it. When a variable is assigned a value, TypeScript infers its type based on the assigned value, and it narrows the type of the variable to match the inferred type. ```typescript let value: string | number; value = 'hello'; if (typeof value === 'string') { console.log(value.toUpperCase()); } value = 42; if (typeof value === 'number') { console.log(value.toFixed(2)); } ``` ================================================ FILE: website/src/content/docs/book/built-in-type-primitives.md ================================================ --- title: Built-in Type Primitives sidebar: order: 49 label: 49. Built-in Type Primitives --- TypeScript has several built-in type primitives that can be used to define variables, function parameters, and return types: * `number`: Represents numeric values, including integers and floating-point numbers. * `string`: Represents textual data * `boolean`: Represents logical values, which can be either true or false. * `null`: Represents the absence of a value. * `undefined`: Represents a value that has not been assigned or has not been defined. * `symbol`: Represents a unique identifier. Symbols are typically used as keys for object properties. * `bigint`: Represents arbitrary-precision integers. * `any`: Represents a dynamic or unknown type. Variables of type any can hold values of any type, and they bypass type checking. * `void`: Represents the absence of any type. It is commonly used as the return type of functions that do not return a value. * `never`: Represents a type for values that never occur. It is typically used as the return type of functions that throw an error or enter an infinite loop. ================================================ FILE: website/src/content/docs/book/class.md ================================================ --- title: Class sidebar: order: 54 label: 54. Class --- ### Class Common Syntax The `class` keyword is used in TypeScript to define a class. Below, you can see an example: ```typescript class Person { private name: string; private age: number; constructor(name: string, age: number) { this.name = name; this.age = age; } public sayHi(): void { console.log( `Hello, my name is ${this.name} and I am ${this.age} years old.` ); } } ``` The `class` keyword is used to define a class named "Person". The class has two private properties: name of type `string` and age of type `number`. The constructor is defined using the `constructor` keyword. It takes name and age as parameters and assigns them to the corresponding properties. The class has a `public` method named sayHi that logs a greeting message. To create an instance of a class in TypeScript, you can use the `new` keyword followed by the class name, followed by parentheses `()`. For instance: ```typescript const myObject = new Person('John Doe', 25); myObject.sayHi(); // Output: Hello, my name is John Doe and I am 25 years old. ``` ### Constructor Constructors are special methods within a class that are used to initialize the object's properties when an instance of the class is created. ```typescript class Person { public name: string; public age: number; constructor(name: string, age: number) { this.name = name; this.age = age; } sayHello() { console.log( `Hello, my name is ${this.name} and I'm ${this.age} years old.` ); } } const john = new Person('Simon', 17); john.sayHello(); ``` It is possible to overload a constructor using the following syntax: ```typescript type Sex = 'm' | 'f'; class Person { name: string; age: number; sex: Sex; constructor(name: string, age: number, sex?: Sex); constructor(name: string, age: number, sex: Sex) { this.name = name; this.age = age; this.sex = sex ?? 'm'; } } const p1 = new Person('Simon', 17); const p2 = new Person('Alice', 22, 'f'); ``` In TypeScript, it is possible to define multiple constructor overloads, but you can have only one implementation that must be compatible with all the overloads, this can be achieved by using an optional parameter. ```typescript class Person { name: string; age: number; constructor(); constructor(name: string); constructor(name: string, age: number); constructor(name?: string, age?: number) { this.name = name ?? 'Unknown'; this.age = age ?? 0; } displayInfo() { console.log(`Name: ${this.name}, Age: ${this.age}`); } } const person1 = new Person(); person1.displayInfo(); // Name: Unknown, Age: 0 const person2 = new Person('John'); person2.displayInfo(); // Name: John, Age: 0 const person3 = new Person('Jane', 25); person3.displayInfo(); // Name: Jane, Age: 25 ``` ### Private and Protected Constructors In TypeScript, constructors can be marked as private or protected, which restricts their accessibility and usage. Private Constructors: Can be called only within the class itself. Private constructors are often used in scenarios where you want to enforce a singleton pattern or restrict the creation of instances to a factory method within the class Protected Constructors: Protected constructors are useful when you want to create a base class that should not be instantiated directly but can be extended by subclasses. ```typescript class BaseClass { protected constructor() {} } class DerivedClass extends BaseClass { private value: number; constructor(value: number) { super(); this.value = value; } } // Attempting to instantiate the base class directly will result in an error // const baseObj = new BaseClass(); // Error: Constructor of class 'BaseClass' is protected. // Create an instance of the derived class const derivedObj = new DerivedClass(10); ``` ### Access Modifiers Access Modifiers `private`, `protected`, and `public` are used to control the visibility and accessibility of class members, such as properties and methods, in TypeScript classes. These modifiers are essential for enforcing encapsulation and establishing boundaries for accessing and modifying the internal state of a class. The `private` modifier restricts access to the class member only within the containing class. The `protected` modifier allows access to the class member within the containing class and its derived classes. The `public` modifier provides unrestricted access to the class member, allowing it to be accessed from anywhere." ### Get and Set Getters and setters are special methods that allow you to define custom access and modification behavior for class properties. They enable you to encapsulate the internal state of an object and provide additional logic when getting or setting the values of properties. In TypeScript, getters and setters are defined using the `get` and `set` keywords respectively. Here's an example: ```typescript class MyClass { private _myProperty: string; constructor(value: string) { this._myProperty = value; } get myProperty(): string { return this._myProperty; } set myProperty(value: string) { this._myProperty = value; } } ``` ### Auto-Accessors in Classes TypeScript version 4.9 adds support for auto-accessors, a forthcoming ECMAScript feature. They resemble class properties but are declared with the "accessor" keyword. ```typescript class Animal { accessor name: string; constructor(name: string) { this.name = name; } } ``` Auto-accessors are "de-sugared" into private `get` and `set` accessors, operating on an inaccessible property. ```typescript class Animal { #__name: string; get name() { return this.#__name; } set name(value: string) { this.#__name = name; } constructor(name: string) { this.name = name; } } ``` ### this In TypeScript, the `this` keyword refers to the current instance of a class within its methods or constructors. It allows you to access and modify the properties and methods of the class from within its own scope. It provides a way to access and manipulate the internal state of an object within its own methods. ```typescript class Person { private name: string; constructor(name: string) { this.name = name; } public introduce(): void { console.log(`Hello, my name is ${this.name}.`); } } const person1 = new Person('Alice'); person1.introduce(); // Hello, my name is Alice. ``` ### Parameter Properties Parameter properties allow you to declare and initialize class properties directly within the constructor parameters avoiding boilerplate code, example: ```typescript class Person { constructor( private name: string, public age: number ) { // The "private" and "public" keywords in the constructor // automatically declare and initialize the corresponding class properties. } public introduce(): void { console.log( `Hello, my name is ${this.name} and I am ${this.age} years old.` ); } } const person = new Person('Alice', 25); person.introduce(); ``` ### Abstract Classes Abstract Classes are used in TypeScript mainly for inheritance, they provide a way to define common properties and methods that can be inherited by subclasses. This is useful when you want to define common behavior and enforce that subclasses implement certain methods. They provide a way to create a hierarchy of classes where the abstract base class provides a shared interface and common functionality for the subclasses. ```typescript abstract class Animal { protected name: string; constructor(name: string) { this.name = name; } abstract makeSound(): void; } class Cat extends Animal { makeSound(): void { console.log(`${this.name} meows.`); } } const cat = new Cat('Whiskers'); cat.makeSound(); // Output: Whiskers meows. ``` ### With Generics Classes with generics allow you to define reusable classes which can work with different types. ```typescript class Container { private item: T; constructor(item: T) { this.item = item; } getItem(): T { return this.item; } setItem(item: T): void { this.item = item; } } const container1 = new Container(42); console.log(container1.getItem()); // 42 const container2 = new Container('Hello'); container2.setItem('World'); console.log(container2.getItem()); // World ``` ### Decorators Decorators provide a mechanism to add metadata, modify behavior, validate, or extend the functionality of the target element. They are functions that execute at runtime. Multiple decorators can be applied to a declaration. Decorators are experimental features, and the following examples are only compatible with TypeScript version 5 or above using ES6. For TypeScript versions prior to 5, they should be enabled using the `experimentalDecorators` property in your `tsconfig.json` or by using `--experimentalDecorators` in your command line (but the following example won't work). Some of the common use cases for decorators include: * Watching property changes. * Watching method calls. * Adding extra properties or methods. * Runtime validation. * Automatic serialization and deserialization. * Logging. * Authorization and authentication. * Error guarding. Note: Decorators for version 5 do not allow decorating parameters. Types of decorators: #### Class Decorators Class Decorators are useful for extending an existing class, such as adding properties or methods, or collecting instances of a class. In the following example, we add a `toString` method that converts the class into a string representation. ```typescript type Constructor = new (...args: any[]) => T; function toString( Value: Class, context: ClassDecoratorContext ) { return class extends Value { constructor(...args: any[]) { super(...args); console.log(JSON.stringify(this)); console.log(JSON.stringify(context)); } }; } @toString class Person { name: string; constructor(name: string) { this.name = name; } greet() { return 'Hello, ' + this.name; } } const person = new Person('Simon'); /* Logs: {"name":"Simon"} {"kind":"class","name":"Person"} */ ``` #### Property Decorator Property decorators are useful for modifying the behavior of a property, such as changing the initialization values. In the following code, we have a script that sets a property to always be in uppercase: ```typescript function upperCase( target: undefined, context: ClassFieldDecoratorContext ) { return function (this: T, value: string) { return value.toUpperCase(); }; } class MyClass { @upperCase prop1 = 'hello!'; } console.log(new MyClass().prop1); // Logs: HELLO! ``` #### Method Decorator Method decorators allow you to change or enhance the behavior of methods. Below is an example of a simple logger: ```typescript function log( target: (this: This, ...args: Args) => Return, context: ClassMethodDecoratorContext< This, (this: This, ...args: Args) => Return > ) { const methodName = String(context.name); function replacementMethod(this: This, ...args: Args): Return { console.log(`LOG: Entering method '${methodName}'.`); const result = target.call(this, ...args); console.log(`LOG: Exiting method '${methodName}'.`); return result; } return replacementMethod; } class MyClass { @log sayHello() { console.log('Hello!'); } } new MyClass().sayHello(); ``` It logs: ```shell LOG: Entering method 'sayHello'. Hello! LOG: Exiting method 'sayHello'. ``` #### Getter and Setter Decorators Getter and setter decorators allow you to change or enhance the behavior of class accessors. They are useful, for instance, for validating property assignments. Here's a simple example for a getter decorator: ```typescript function range(min: number, max: number) { return function ( target: (this: This) => Return, context: ClassGetterDecoratorContext ) { return function (this: This): Return { const value = target.call(this); if (value < min || value > max) { throw 'Invalid'; } Object.defineProperty(this, context.name, { value, enumerable: true, }); return value; }; }; } class MyClass { private _value = 0; constructor(value: number) { this._value = value; } @range(1, 100) get getValue(): number { return this._value; } } const obj = new MyClass(10); console.log(obj.getValue); // Valid: 10 const obj2 = new MyClass(999); console.log(obj2.getValue); // Throw: Invalid! ``` #### Decorator Metadata Decorator Metadata simplifies the process for decorators to apply and utilize metadata in any class. They can access a new metadata property on the context object, which can serve as a key for both primitives and objects. Metadata information can be accessed on the class via `Symbol.metadata`. Metadata can be used for various purposes, such as debugging, serialization, or dependency injection with decorators. ```typescript //@ts-ignore Symbol.metadata ??= Symbol('Symbol.metadata'); // Simple polify type Context = | ClassFieldDecoratorContext | ClassAccessorDecoratorContext | ClassMethodDecoratorContext; // Context contains property metadata: DecoratorMetadata function setMetadata(_target: any, context: Context) { // Set the metadata object with a primitive value context.metadata[context.name] = true; } class MyClass { @setMetadata a = 123; @setMetadata accessor b = 'b'; @setMetadata fn() {} } const metadata = MyClass[Symbol.metadata]; // Get metadata information console.log(JSON.stringify(metadata)); // {"bar":true,"baz":true,"foo":true} ``` ### Inheritance Inheritance refers to the mechanism by which a class can inherit properties and methods from another class, known as the base class or superclass. The derived class, also called the child class or subclass, can extend and specialize the functionality of the base class by adding new properties and methods or overriding existing ones. ```typescript class Animal { name: string; constructor(name: string) { this.name = name; } speak(): void { console.log('The animal makes a sound'); } } class Dog extends Animal { breed: string; constructor(name: string, breed: string) { super(name); this.breed = breed; } speak(): void { console.log('Woof! Woof!'); } } // Create an instance of the base class const animal = new Animal('Generic Animal'); animal.speak(); // The animal makes a sound // Create an instance of the derived class const dog = new Dog('Max', 'Labrador'); dog.speak(); // Woof! Woof!" ``` TypeScript does not support multiple inheritance in the traditional sense and instead allows inheritance from a single base class. TypeScript supports multiple interfaces. An interface can define a contract for the structure of an object, and a class can implement multiple interfaces. This allows a class to inherit behavior and structure from multiple sources. ```typescript interface Flyable { fly(): void; } interface Swimmable { swim(): void; } class FlyingFish implements Flyable, Swimmable { fly() { console.log('Flying...'); } swim() { console.log('Swimming...'); } } const flyingFish = new FlyingFish(); flyingFish.fly(); flyingFish.swim(); ``` The `class` keyword in TypeScript, similar to JavaScript, is often referred to as syntactic sugar. It was introduced in ECMAScript 2015 (ES6) to offer a more familiar syntax for creating and working with objects in a class-based manner. However, it's important to note that TypeScript, being a superset of JavaScript, ultimately compiles down to JavaScript, which remains prototype-based at its core. ### Statics TypeScript has static members. To access the static members of a class, you can use the class name followed by a dot, without the need to create an object. ```typescript class OfficeWorker { static memberCount: number = 0; constructor(private name: string) { OfficeWorker.memberCount++; } } const w1 = new OfficeWorker('James'); const w2 = new OfficeWorker('Simon'); const total = OfficeWorker.memberCount; console.log(total); // 2 ``` ### Property initialization There are several ways how you can initialize properties for a class in TypeScript: Inline: In the following example these initial values will be used when an instance of the class is created. ```typescript class MyClass { property1: string = 'default value'; property2: number = 42; } ``` In the constructor: ```typescript class MyClass { property1: string; property2: number; constructor() { this.property1 = 'default value'; this.property2 = 42; } } ``` Using constructor parameters: ```typescript class MyClass { constructor( private property1: string = 'default value', public property2: number = 42 ) { // There is no need to assign the values to the properties explicitly. } log() { console.log(this.property2); } } const x = new MyClass(); x.log(); ``` ### Method overloading Method overloading allows a class to have multiple methods with the same name but different parameter types or a different number of parameters. This allows us to call a method in different ways based on the arguments passed. ```typescript class MyClass { add(a: number, b: number): number; // Overload signature 1 add(a: string, b: string): string; // Overload signature 2 add(a: number | string, b: number | string): number | string { if (typeof a === 'number' && typeof b === 'number') { return a + b; } if (typeof a === 'string' && typeof b === 'string') { return a.concat(b); } throw new Error('Invalid arguments'); } } const r = new MyClass(); console.log(r.add(10, 5)); // Logs 15 ``` ================================================ FILE: website/src/content/docs/book/common-built-in-js-objects.md ================================================ --- title: Common Built-in JS Objects sidebar: order: 50 label: 50. Common Built-in JS Objects --- TypeScript is a superset of JavaScript, it includes all the commonly used built-in JavaScript objects. You can find an extensive list of these objects on the Mozilla Developer Network (MDN) documentation website: [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects) Here is a list of some commonly used built-in JavaScript objects: * Function * Object * Boolean * Error * Number * BigInt * Math * Date * String * RegExp * Array * Map * Set * Promise * Intl ================================================ FILE: website/src/content/docs/book/conditional-types.md ================================================ --- title: Conditional Types sidebar: order: 39 label: 39. Conditional Types --- Conditional Types are a way to create a type that depends on a condition, where the type to be created is determined based on the result of the condition. They are defined using the `extends` keyword and a ternary operator to conditionally choose between two types. ```typescript type IsArray = T extends any[] ? true : false; const myArray = [1, 2, 3]; const myNumber = 42; type IsMyArrayAnArray = IsArray; // Type true type IsMyNumberAnArray = IsArray; // Type false ``` ================================================ FILE: website/src/content/docs/book/control-flow-analysis.md ================================================ --- title: Control Flow Analysis sidebar: order: 22 label: 22. Control Flow Analysis --- Control Flow Analysis in TypeScript is a way to statically analyze the code flow to infer the types of variables, allowing the compiler to narrow the types of those variables as needed, based on the results of the analysis. Prior to TypeScript 4.4, code flow analysis would only be applied to code within an if statement, but from TypeScript 4.4, it can also be applied to conditional expressions and discriminant property accesses indirectly referenced through const variables. For example: ```typescript const f1 = (x: unknown) => { const isString = typeof x === 'string'; if (isString) { x.length; } }; const f2 = ( obj: { kind: 'foo'; foo: string } | { kind: 'bar'; bar: number } ) => { const isFoo = obj.kind === 'foo'; if (isFoo) { obj.foo; } else { obj.bar; } }; ``` Some examples where narrowing does not occur: ```typescript const f1 = (x: unknown) => { let isString = typeof x === 'string'; if (isString) { x.length; // Error, no narrowing because isString it is not const } }; const f6 = ( obj: { kind: 'foo'; foo: string } | { kind: 'bar'; bar: number } ) => { const isFoo = obj.kind === 'foo'; obj = obj; if (isFoo) { obj.foo; // Error, no narrowing because obj is assigned in function body } }; ``` Notes: Up to five levels of indirection are analyzed in conditional expressions. ================================================ FILE: website/src/content/docs/book/differences-between-type-and-interface.md ================================================ --- title: Differences between Type and Interface sidebar: order: 53 label: 53. Differences between Type and Interface --- Declaration merging (augmentation): Interfaces support declaration merging, which means that you can define multiple interfaces with the same name, and TypeScript will merge them into a single interface with the combined properties and methods. On the other hand, types do not support declaration merging. This can be helpful when you want to add extra functionality or customize existing types without modifying the original definitions or patching missing or incorrect types. ```typescript interface A { x: string; } interface A { y: string; } const j: A = { x: 'xx', y: 'yy', }; ``` Extending other types/interfaces: Both types and interfaces can extend other types/interfaces, but the syntax is different. With interfaces, you use the `extends` keyword to inherit properties and methods from other interfaces. However, an interface cannot extend a complex type like a union type. ```typescript interface A { x: string; y: number; } interface B extends A { z: string; } const car: B = { x: 'x', y: 123, z: 'z', }; ``` For types, you use the & operator to combine multiple types into a single type (intersection). ```typescript interface A { x: string; y: number; } type B = A & { j: string; }; const c: B = { x: 'x', y: 123, j: 'j', }; ``` Union and Intersection Types: Types are more flexible when it comes to defining Union and Intersection Types. With the `type` keyword, you can easily create union types using the `|` operator and intersection types using the `&` operator. While interfaces can also represent union types indirectly, they don't have built-in support for intersection types. ```typescript type Department = 'dep-x' | 'dep-y'; // Union type Person = { name: string; age: number; }; type Employee = { id: number; department: Department; }; type EmployeeInfo = Person & Employee; // Intersection ``` Example with interfaces: ```typescript interface A { x: 'x'; } interface B { y: 'y'; } type C = A | B; // Union of interfaces ``` ================================================ FILE: website/src/content/docs/book/discriminated-unions.md ================================================ --- title: Discriminated Unions sidebar: order: 24 label: 24. Discriminated Unions --- Discriminated Unions in TypeScript are a type of union type that uses a common property, known as the discriminant, to narrow down the set of possible types for the union. ```typescript type Square = { kind: 'square'; // Discriminant size: number; }; type Circle = { kind: 'circle'; // Discriminant radius: number; }; type Shape = Square | Circle; const area = (shape: Shape) => { switch (shape.kind) { case 'square': return Math.pow(shape.size, 2); case 'circle': return Math.PI * Math.pow(shape.radius, 2); } }; const square: Square = { kind: 'square', size: 5 }; const circle: Circle = { kind: 'circle', radius: 2 }; console.log(area(square)); // 25 console.log(area(circle)); // 12.566370614359172 ``` ================================================ FILE: website/src/content/docs/book/distributive-conditional-types.md ================================================ --- title: Distributive Conditional Types sidebar: order: 40 label: 40. Distributive Conditional Types --- Distributive Conditional Types are a feature that allow a type to be distributed over a union of types, by applying a transformation to each member of the union individually. This can be especially useful when working with mapped types or higher-order types. ```typescript type Nullable = T extends any ? T | null : never; type NumberOrBool = number | boolean; type NullableNumberOrBool = Nullable; // number | boolean | null ``` ================================================ FILE: website/src/content/docs/book/downloads-and-website.md ================================================ --- title: Downloads and website sidebar: order: 3 label: 3. Downloads and website --- You can also download the Epub version: [https://github.com/gibbok/typescript-book/tree/main/downloads](https://github.com/gibbok/typescript-book/tree/main/downloads) An online version is available at: [https://gibbok.github.io/typescript-book](https://gibbok.github.io/typescript-book) ================================================ FILE: website/src/content/docs/book/enums.md ================================================ --- title: Enums sidebar: order: 19 label: 19. Enums --- In TypeScript, an `enum` is a set of named constant values. ```typescript enum Color { Red = '#ff0000', Green = '#00ff00', Blue = '#0000ff', } ``` Enums can be defined in different ways: ### Numeric enums In TypeScript, a Numeric Enum is an Enum where each constant is assigned a numeric value, starting from 0 by default. ```typescript enum Size { Small, // value starts from 0 Medium, Large, } ``` It is possible to specify custom values by explicitly assigning them: ```typescript enum Size { Small = 10, Medium, Large, } console.log(Size.Medium); // 11 ``` ### String enums In TypeScript, a String enum is an Enum where each constant is assigned a string value. ```typescript enum Language { English = 'EN', Spanish = 'ES', } ``` Note: TypeScript allows the usage of heterogeneous Enums where string and numeric members can coexist. ### Constant enums A constant enum in TypeScript is a special type of Enum where all the values are known at compile time and are inlined wherever the enum is used, resulting in more efficient code. ```typescript const enum Language { English = 'EN', Spanish = 'ES', } console.log(Language.English); ``` Will be compiled into: ```typescript console.log('EN' /* Language.English */); ``` Notes: Const Enums have hardcoded values, erasing the Enum, which can be more efficient in self-contained libraries but is generally not desirable. Also, Const enums cannot have computed members. ### Reverse mapping In TypeScript, reverse mappings in Enums refer to the ability to retrieve the Enum member name from its value. By default, Enum members have forward mappings from name to value, but reverse mappings can be created by explicitly setting values for each member. Reverse mappings are useful when you need to look up an Enum member by its value, or when you need to iterate over all the Enum members. Note that only numeric enums members will generate reverse mappings, while String Enum members do not get a reverse mapping generated at all. The following enum: ```typescript enum Grade { A = 90, B = 80, C = 70, F = 'fail', } ``` Compiles to: ```javascript 'use strict'; var Grade; (function (Grade) { Grade[(Grade['A'] = 90)] = 'A'; Grade[(Grade['B'] = 80)] = 'B'; Grade[(Grade['C'] = 70)] = 'C'; Grade['F'] = 'fail'; })(Grade || (Grade = {})); ``` Therefore, mapping values to keys works for numeric enum members, but not for string enum members: ```typescript enum Grade { A = 90, B = 80, C = 70, F = 'fail', } const myGrade = Grade.A; console.log(Grade[myGrade]); // A console.log(Grade[90]); // A const failGrade = Grade.F; console.log(failGrade); // fail console.log(Grade[failGrade]); // Element implicitly has an 'any' type because index expression is not of type 'number'. ``` ### Ambient enums An ambient enum in TypeScript is a type of Enum that is defined in a declaration file (*.d.ts) without an associated implementation. It allows you to define a set of named constants that can be used in a type-safe way across different files without having to import the implementation details in each file. ### Computed and constant members In TypeScript, a computed member is a member of an Enum that has a value calculated at runtime, while a constant member is a member whose value is set at compile-time and cannot be changed during runtime. Computed members are allowed in regular Enums, while constant members are allowed in both regular and const enums. ```typescript // Constant members enum Color { Red = 1, Green = 5, Blue = Red + Green, } console.log(Color.Blue); // 6 generation at compilation time ``` ```typescript // Computed members enum Color { Red = 1, Green = Math.pow(2, 2), Blue = Math.floor(Math.random() * 3) + 1, } console.log(Color.Blue); // random number generated at run time ``` Enums are denoted by unions comprising their member types. The values of each member can be determined through constant or non-constant expressions, with members possessing constant values being assigned literal types. To illustrate, consider the declaration of type E and its subtypes E.A, E.B, and E.C. In this case, E represents the union E.A | E.B | E.C. ```typescript const identity = (value: number) => value; enum E { A = 2 * 5, // Numeric literal B = 'bar', // String literal C = identity(42), // Opaque computed } console.log(E.C); //42 ``` ================================================ FILE: website/src/content/docs/book/erased-structural-types.md ================================================ --- title: Erased Structural Types sidebar: order: 56 label: 56. Erased Structural Types --- In TypeScript, objects do not have to match a specific, exact type. For instance, if we create an object that fulfills an interface's requirements, we can utilize that object in places where that interface is required, even if there was no explicit connection between them. Example: ```typescript type NameProp1 = { prop1: string; }; function log(x: NameProp1) { console.log(x.prop1); } const obj = { prop2: 123, prop1: 'Origin', }; log(obj); // Valid ``` ================================================ FILE: website/src/content/docs/book/exhaustiveness-checking.md ================================================ --- title: Exhaustiveness checking sidebar: order: 26 label: 26. Exhaustiveness checking --- Exhaustiveness checking is a feature in TypeScript that ensures all possible cases of a discriminated union are handled in a `switch` statement or an `if` statement. ```typescript type Direction = 'up' | 'down'; const move = (direction: Direction) => { switch (direction) { case 'up': console.log('Moving up'); break; case 'down': console.log('Moving down'); break; default: const exhaustiveCheck: never = direction; console.log(exhaustiveCheck); // This line will never be executed } }; ``` The `never` type is used to ensure that the default case is exhaustive and that TypeScript will raise an error if a new value is added to the Direction type without being handled in the switch statement. ================================================ FILE: website/src/content/docs/book/exploring-the-type-system.md ================================================ --- title: Exploring the Type System sidebar: order: 9 label: 9. Exploring the Type System --- ### The TypeScript Language Service The TypeScript Language Service, also known as tsserver, offers various features such as error reporting, diagnostics, compile-on-save, renaming, go to definition, completion lists, signature help, and more. It is primarily used by integrated development environments (IDEs) to provide IntelliSense support. It seamlessly integrates with Visual Studio Code and is utilized by tools like Conquer of Completion (Coc). Developers can leverage a dedicated API and create their own custom language service plugins to enhance the TypeScript editing experience. This can be particularly useful for implementing special linting features or enabling auto-completion for a custom templating language. An example of a real-world custom plugin is "typescript-styled-plugin", which provides syntax error reporting and IntelliSense support for CSS properties in styled components. For more information and quick start guides, you can refer to the official TypeScript Wiki on GitHub: [https://github.com/microsoft/TypeScript/wiki/](https://github.com/microsoft/TypeScript/wiki/) ### Structural Typing TypeScript is based on a structural type system. This means that the compatibility and equivalence of types are determined by the type's actual structure or definition, rather than its name or place of declaration, as in nominative type systems like C# or C. TypeScript's structural type system was designed based on how JavaScript's dynamic duck typing system works during runtime. The following example is valid TypeScript code. As you can observe, "X" and "Y" have the same member "a," even though they have different declaration names. The types are determined by their structures, and in this case, since the structures are the same, they are compatible and valid. ```typescript type X = { a: string; }; type Y = { a: string; }; const x: X = { a: 'a' }; const y: Y = x; // Valid ``` ### TypeScript Fundamental Comparison Rules The TypeScript comparison process is recursive and executed on types nested at any level. A type "X" is compatible with "Y" if "Y" has at least the same members as "X". ```typescript type X = { a: string; }; const y = { a: 'A', b: 'B' }; // Valid, as it has at least the same members as X const r: X = y; ``` Function parameters are compared by types, not by their names: ```typescript type X = (a: number) => void; type Y = (a: number) => void; let x: X = (j: number) => undefined; let y: Y = (k: number) => undefined; y = x; // Valid x = y; // Valid ``` Function return types must be the same: ```typescript type X = (a: number) => undefined; type Y = (a: number) => number; let x: X = (a: number) => undefined; let y: Y = (a: number) => 1; y = x; // Invalid x = y; // Invalid ``` The return type of a source function must be a subtype of the return type of a target function: ```typescript let x = () => ({ a: 'A' }); let y = () => ({ a: 'A', b: 'B' }); x = y; // Valid y = x; // Invalid member b is missing ``` Discarding function parameters is allowed, as it is a common practice in JavaScript, for instance using "Array.prototype.map()": ```typescript [1, 2, 3].map((element, _index, _array) => element + 'x'); ``` Therefore, the following type declarations are completely valid: ```typescript type X = (a: number) => undefined; type Y = (a: number, b: number) => undefined; let x: X = (a: number) => undefined; let y: Y = (a: number) => undefined; // Missing b parameter y = x; // Valid ``` Any additional optional parameters of the source type are valid: ```typescript type X = (a: number, b?: number, c?: number) => undefined; type Y = (a: number) => undefined; let x: X = a => undefined; let y: Y = a => undefined; y = x; // Valid x = y; //Valid ``` Any optional parameters of the target type without corresponding parameters in the source type are valid and not an error: ```typescript type X = (a: number) => undefined; type Y = (a: number, b?: number) => undefined; let x: X = a => undefined; let y: Y = a => undefined; y = x; // Valid x = y; // Valid ``` The rest parameter is treated as an infinite series of optional parameters: ```typescript type X = (a: number, ...rest: number[]) => undefined; let x: X = a => undefined; //valid ``` Functions with overloads are valid if the overload signature is compatible with its implementation signature: ```typescript function x(a: string): void; function x(a: string, b: number): void; function x(a: string, b?: number): void { console.log(a, b); } x('a'); // Valid x('a', 1); // Valid function y(a: string): void; // Invalid, not compatible with implementation signature function y(a: string, b: number): void; function y(a: string, b: number): void { console.log(a, b); } y('a'); y('a', 1); ``` Function parameter comparison succeeds if the source and target parameters are assignable to supertypes or subtypes (bivariance). ```typescript // Supertype class X { a: string; constructor(value: string) { this.a = value; } } // Subtype class Y extends X {} // Subtype class Z extends X {} type GetA = (x: X) => string; const getA: GetA = x => x.a; // Bivariance does accept supertypes console.log(getA(new X('x'))); // Valid console.log(getA(new Y('Y'))); // Valid console.log(getA(new Z('z'))); // Valid ``` Enums are comparable and valid with numbers and vice versa, but comparing Enum values from different Enum types is invalid. ```typescript enum X { A, B, } enum Y { A, B, C, } const xa: number = X.A; // Valid const ya: Y = 0; // Valid X.A === Y.A; // Invalid ``` Instances of a class are subject to a compatibility check for their private and protected members: ```typescript class X { public a: string; constructor(value: string) { this.a = value; } } class Y { private a: string; constructor(value: string) { this.a = value; } } let x: X = new Y('y'); // Invalid ``` The comparison check does not take into consideration the different inheritance hierarchy, for instance: ```typescript class X { public a: string; constructor(value: string) { this.a = value; } } class Y extends X { public a: string; constructor(value: string) { super(value); this.a = value; } } class Z { public a: string; constructor(value: string) { this.a = value; } } let x: X = new X('x'); let y: Y = new Y('y'); let z: Z = new Z('z'); x === y; // Valid x === z; // Valid even if z is from a different inheritance hierarchy ``` Generics are compared using their structures based on the resulting type after applying the generic parameter, only the final result is compared as a non-generic type. ```typescript interface X { a: T; } let x: X = { a: 1 }; let y: X = { a: 'a' }; x === y; // Invalid as the type argument is used in the final structure ``` ```typescript interface X {} const x: X = 1; const y: X = 'a'; x === y; // Valid as the type argument is not used in the final structure ``` When generics do not have their type argument specified, all the unspecified arguments are treated as types with "any": ```typescript type X = (x: T) => T; type Y = (y: K) => K; let x: X = x => x; let y: Y = y => y; x = y; // Valid ``` Remember: ```typescript let a: number = 1; let b: number = 2; a = b; // Valid, everything is assignable to itself let c: any; c = 1; // Valid, all types are assignable to any let d: unknown; d = 1; // Valid, all types are assignable to unknown let e: unknown; let e1: unknown = e; // Valid, unknown is only assignable to itself and any let e2: any = e; // Valid let e3: number = e; // Invalid let f: never; f = 1; // Invalid, nothing is assignable to never let g: void; let g1: any; g = 1; // Invalid, void is not assignable to or from anything expect any g = g1; // Valid ``` Please note that when "strictNullChecks" is enabled, "null" and "undefined" are treated similarly to "void"; otherwise, they are similar to "never". ### Types as Sets In TypeScript, a type is a set of possible values. This set is also referred to as the domain of the type. Each value of a type can be viewed as an element in a set. A type establishes the constraints that every element in the set must satisfy to be considered a member of that set. The primary task of TypeScript is to check and verify whether one set is a subset of another. TypeScript supports various types of sets: | Set term | TypeScript | Notes | | ------------------ | ------------------------------- | ------------------------------------------------------------------------------------------------------------------ | | Empty set | never | "never" contains anything apart itself | | Single element set | undefined / null / literal type | | | Finite set | boolean / union | | | Infinite set | string / number / object | | | Universal set | any / unknown | Every element is a member of "any" and every set is a subset of it / "unknown" is a type-safe counterpart of "any" | Here few examples: | TypeScript | Set term | Example | | --------------------- | ---------------------- | ------------------------------------------------------------------------------- | | never | ∅ (empty set) | const x: never = 'x'; // Error: Type 'string' is not assignable to type 'never' | | | | | Literal type | Single element set | type X = 'X'; | | | | type Y = 7; | | | | | Value assignable to T | Value ∈ T (member of) | type XY = 'X' \| 'Y'; | | | | const x: XY = 'X'; | | | | | T1 assignable to T2 | T1 ⊆ T2 (subset of) | type XY = 'X' \| 'Y'; | | | | const x: XY = 'X'; | | | | const j: XY = 'J'; // Type '"J"' is not assignable to type 'XY'. | | | | | | T1 extends T2 | T1 ⊆ T2 (subset of) | type X = 'X' extends string ? true : false; | | | | | T1 \| T2 | T1 ∪ T2 (union) | type XY = 'X' \| 'Y'; | | | | type JK = 1 \| 2; | | | | | T1 & T2 | T1 ∩ T2 (intersection) | type X = \{ a: string \} | | | | type Y = \{ b: string \} | | | | type XY = X & Y | | | | const x: XY = \{ a: 'a', b: 'b' \} | | | | | unknown | Universal set | const x: unknown = 1 | An union, (T1 | T2) creates a wider set (both): ```typescript type X = { a: string; }; type Y = { b: string; }; type XY = X | Y; const r: XY = { a: 'a', b: 'x' }; // Valid ``` An intersection, (T1 & T2) create a narrower set (only shared): ```typescript type X = { a: string; }; type Y = { a: string; b: string; }; type XY = X & Y; const r: XY = { a: 'a' }; // Invalid const j: XY = { a: 'a', b: 'b' }; // Valid ``` The `extends` keyword could be considered as a "subset of" in this context. It sets a constraint for a type. The extends used with a generic, take the generic as an infinite set and it will constrain it to a more specific type. Please note that `extends` has nothing to do with hierarchy in a OOP sense (there is no this concept in TypeScript). TypeScript works with sets and does not have a strict hierarchy, infact, as in the example below, two types could overlap without either being a subtype of the other type (TypeScript considers the structure, shape of the objects). ```typescript interface X { a: string; } interface Y extends X { b: string; } interface Z extends Y { c: string; } const z: Z = { a: 'a', b: 'b', c: 'c' }; interface X1 { a: string; } interface Y1 { a: string; b: string; } interface Z1 { a: string; b: string; c: string; } const z1: Z1 = { a: 'a', b: 'b', c: 'c' }; const r: Z1 = z; // Valid ``` ### Assign a type: Type Declarations and Type Assertions A type can be assigned in different ways in TypeScript: #### Type Declaration In the following example, we use x: X (": Type") to declare a type for the variable x. ```typescript type X = { a: string; }; // Type declaration const x: X = { a: 'a', }; ``` If the variable is not in the specified format, TypeScript will report an error. For instance: ```typescript type X = { a: string; }; const x: X = { a: 'a', b: 'b', // Error: Object literal may only specify known properties }; ``` #### Type Assertion It is possible to add an assertion by using the `as` keyword. This tells the compiler that the developer has more information about a type and silences any errors that may occur. For example: ```typescript type X = { a: string; }; const x = { a: 'a', b: 'b', } as X; ``` In the above example, the object x is asserted to have the type X using the as keyword. This informs the TypeScript compiler that the object conforms to the specified type, even though it has an additional property b not present in the type definition. Type assertions are useful in situations where a more specific type needs to be specified, especially when working with the DOM. For instance: ```typescript const myInput = document.getElementById('my_input') as HTMLInputElement; ``` Here, the type assertion as HTMLInputElement is used to tell TypeScript that the result of getElementById should be treated as an HTMLInputElement. Type assertions can also be used to remap keys, as shown in the example below with template literals: ```typescript type J = { [Property in keyof Type as `prefix_${string & Property}`]: () => Type[Property]; }; type X = { a: string; b: number; }; type Y = J; ``` In this example, the type `J` uses a mapped type with a template literal to remap the keys of Type. It creates new properties with a "prefix_" added to each key, and their corresponding values are functions returning the original property values. It is worth noting that when using a type assertion, TypeScript will not execute excess property checking. Therefore, it is generally preferable to use a Type Declaration when the structure of the object is known in advance. #### Ambient Declarations Ambient declarations are files that describe types for JavaScript code, they have a file name format as `.d.ts.`. They are usually imported and used to annotate existing JavaScript libraries or to add types to existing JS files in your project. Many common libraries types can be found at: [https://github.com/DefinitelyTyped/DefinitelyTyped/](https://github.com/DefinitelyTyped/DefinitelyTyped/) and can be installed using: ```shell npm install --save-dev @types/library-name ``` For your defined Ambient Declarations, you can import using the "triple-slash" reference: ```typescript /// ``` You can use Ambient Declarations even within JavaScript files using `// @ts-check`. The `declare` keyword enables type definitions for existing JavaScript code without importing it, serving as a placeholder for types from another file or globally. ### Property Checking and Excess Property Checking TypeScript is based on a structural type system but excess property checking is a property of TypeScript which allows it to check whether an object has the exact properties specified in the type. Excess Property Checking is performed when assigning object literals to variables or when passing them as arguments to the function's excess property, for instance. ```typescript type X = { a: string; }; const y = { a: 'a', b: 'b' }; const x: X = y; // Valid because structural typing const w: X = { a: 'a', b: 'b' }; // Invalid because excess property checking ``` ### Weak Types A type is considered weak when it contains nothing but a set of all-optional properties: ```typescript type X = { a?: string; b?: string; }; ``` TypeScript considers an error to assign anything to a weak type when there is no overlap, for instance, the following throws an error: ```typescript type Options = { a?: string; b?: string; }; const fn = (options: Options) => undefined; fn({ c: 'c' }); // Invalid ``` Although not recommended, if needed, it is possible to bypass this check by using type assertion: ```typescript type Options = { a?: string; b?: string; }; const fn = (options: Options) => undefined; fn({ c: 'c' } as Options); // Valid ``` Or by adding `unknown` to the index signature to the weak type: ```typescript type Options = { [prop: string]: unknown; a?: string; b?: string; }; const fn = (options: Options) => undefined; fn({ c: 'c' }); // Valid ``` ### Strict Object Literal Checking (Freshness) Strict object literal checking, sometimes referred to as "freshness", is a feature in TypeScript that helps catch excess or misspelled properties that would otherwise go unnoticed in normal structural type checks. When creating an object literal, the TypeScript compiler considers it "fresh." If the object literal is assigned to a variable or passed as a parameter, TypeScript will throw an error if the object literal specifies properties that do not exist in the target type. However, "freshness" disappears when an object literal is widened or a type assertion is used. Here are some examples to illustrate: ```typescript type X = { a: string }; type Y = { a: string; b: string }; let x: X; x = { a: 'a', b: 'b' }; // Freshness check: Invalid assignment var y: Y; y = { a: 'a', bx: 'bx' }; // Freshness check: Invalid assignment const fn = (x: X) => console.log(x.a); fn(x); fn(y); // Widening: No errors, structurally type compatible fn({ a: 'a', bx: 'b' }); // Freshness check: Invalid argument let c: X = { a: 'a' }; let d: Y = { a: 'a', b: '' }; c = d; // Widening: No Freshness check ``` ### Type Inference TypeScript can infer types when no annotation is provided during: * Variable initialization. * Member initialization. * Setting defaults for parameters. * Function return type. For example: ```typescript let x = 'x'; // The type inferred is string ``` The TypeScript compiler analyzes the value or expression and determines its type based on the available information. ### More Advanced Inferences When multiple expressions are used in type inference, TypeScript looks for the "best common types." For instance: ```typescript let x = [1, 'x', 1, null]; // The type inferred is: (string | number | null)[] ``` If the compiler cannot find the best common types, it returns a union type. For example: ```typescript let x = [new RegExp('x'), new Date()]; // Type inferred is: (RegExp | Date)[] ``` TypeScript utilizes "contextual typing" based on the variable's location to infer types. In the following example, the compiler knows that `e` is of type `MouseEvent` because of the `click` event type defined in the lib.d.ts file, which contains ambient declarations for various common JavaScript constructs and the DOM: ```typescript window.addEventListener('click', function (e) {}); // The inferred type of e is MouseEvent ``` ### Type Widening Type widening is the process in which TypeScript assigns a type to a variable initialized when no type annotation was provided. It allows narrow to wider types but not vice versa. In the following example: ```typescript let x = 'x'; // TypeScript infers as string, a wide type let y: 'y' | 'x' = 'y'; // y types is a union of literal types y = x; // Invalid Type 'string' is not assignable to type '"x" | "y"'. ``` TypeScript assigns `string` to `x` based on the single value provided during initialization (`x`), this is an example of widening. TypeScript provides ways to have control of the widening process, for instance using "const". ### Const Using the `const` keyword when declaring a variable results in a narrower type inference in TypeScript. For example: ```typescript const x = 'x'; // TypeScript infers the type of x as 'x', a narrower type let y: 'y' | 'x' = 'y'; y = x; // Valid: The type of x is inferred as 'x' ``` By using `const` to declare the variable x, its type is narrowed to the specific literal value 'x'. Since the type of x is narrowed, it can be assigned to the variable y without any error. The reason the type can be inferred is because `const` variables cannot be reassigned, so their type can be narrowed down to a specific literal type, in this case, the literal type 'x'. #### Const Modifier on Type Parameters From version 5.0 of TypeScript, it is possible to specify the `const` attribute on a generic type parameter. This allows for inferring the most precise type possible. Let's see an example without using `const`: ```typescript function identity(value: T) { // No const here return value; } const values = identity({ a: 'a', b: 'b' }); // Type infered is: { a: string; b: string; } ``` As you can see, the properties `a` and `b` are inferred with a type of `string` . Now, let's see the difference with the `const` version: ```typescript function identity(value: T) { // Using const modifier on type parameters return value; } const values = identity({ a: 'a', b: 'b' }); // Type infered is: { a: "a"; b: "b"; } ``` Now we can see that the properties `a` and `b` are inferred as `const`, so `a` and `b` are treated as string literals rather than just `string` types. #### Const assertion This feature allows you to declare a variable with a more precise literal type based on its initialization value, signifying to the compiler that the value should be treated as an immutable literal. Here are a few examples: On a single property: ```typescript const v = { x: 3 as const, }; v.x = 3; ``` On an entire object: ```typescript const v = { x: 1, y: 2, } as const; ``` This can be particularly useful when defining the type for a tuple: ```typescript const x = [1, 2, 3]; // number[] const y = [1, 2, 3] as const; // Tuple of readonly [1, 2, 3] ``` ### Explicit Type Annotation We can be specific and pass a type, in the following example property `x` is of type `number`: ```typescript const v = { x: 1, // Inferred type: number (widening) }; v.x = 3; // Valid ``` We can make the type annotation more specific by using a union of literal types: ```typescript const v: { x: 1 | 2 | 3 } = { x: 1, // x is now a union of literal types: 1 | 2 | 3 }; v.x = 3; // Valid v.x = 100; // Invalid ``` ### Type Narrowing Type Narrowing is the process in TypeScript where a general type is narrowed down to a more specific type. This occurs when TypeScript analyzes the code and determines that certain conditions or operations can refine the type information. Narrowing types can occur in different ways, including: #### Conditions By using conditional statements, such as `if` or `switch`, TypeScript can narrow down the type based on the outcome of the condition. For example: ```typescript let x: number | undefined = 10; if (x !== undefined) { x += 100; // The type is number, which had been narrowed by the condition } ``` #### Throwing or returning Throwing an error or returning early from a branch can be used to help TypeScript narrow down a type. For example: ```typescript let x: number | undefined = 10; if (x === undefined) { throw 'error'; } x += 100; ``` Other ways to narrow down types in TypeScript include: * `instanceof` operator: Used to check if an object is an instance of a specific class. * `in` operator: Used to check if a property exists in an object. * `typeof` operator: Used to check the type of a value at runtime. * Built-in functions like `Array.isArray()`: Used to check if a value is an array. #### Discriminated Union Using a "Discriminated Union" is a pattern in TypeScript where an explicit "tag" is added to objects to distinguish between different types within a union. This pattern is also referred to as a "tagged union." In the following example, the "tag" is represented by the property "type": ```typescript type A = { type: 'type_a'; value: number }; type B = { type: 'type_b'; value: string }; const x = (input: A | B): string | number => { switch (input.type) { case 'type_a': return input.value + 100; // type is A case 'type_b': return input.value + 'extra'; // type is B } }; ``` #### User-Defined Type Guards In cases where TypeScript is unable to determine a type, it is possible to write a helper function known as a "user-defined type guard." In the following example, we will utilize a Type Predicate to narrow down the type after applying certain filtering: ```typescript const data = ['a', null, 'c', 'd', null, 'f']; const r1 = data.filter(x => x != null); // The type is (string | null)[], TypeScript was not able to infer the type properly const isValid = (item: string | null): item is string => item !== null; // Custom type guard const r2 = data.filter(isValid); // The type is fine now string[], by using the predicate type guard we were able to narrow the type ``` ================================================ FILE: website/src/content/docs/book/extending-types.md ================================================ --- title: Extending Types sidebar: order: 15 label: 15. Extending Types --- It is possible to extend an `interface` (copy members from another type): ```typescript interface X { a: string; } interface Y extends X { b: string; } ``` It is also possible to extend from multiple types: ```typescript interface A { a: string; } interface B { b: string; } interface Y extends A, B { y: string; } ``` The `extends` keyword works only on interfaces and classes, for types use an intersection: ```typescript type A = { a: number; }; type B = { b: number; }; type C = A & B; ``` It is possible to extend a type using an inference but not vice versa: ```typescript type A = { a: string; }; interface B extends A { b: string; } ``` ================================================ FILE: website/src/content/docs/book/fixed-length-tuple.md ================================================ --- title: Fixed Length Tuple sidebar: order: 30 label: 30. Fixed Length Tuple --- A Fixed Length Tuple is a specific type of tuple that enforces a fixed number of elements of specific types, and disallows any modifications to the length of the tuple once it is defined. Fixed Length Tuples are useful when you need to represent a collection of values with a specific number of elements and specific types, and you want to ensure that the length and types of the tuple cannot be changed inadvertently. ```typescript const x = [10, 'hello'] as const; x.push(2); // Error ``` ================================================ FILE: website/src/content/docs/book/generics.md ================================================ --- title: Generics sidebar: order: 55 label: 55. Generics --- Generics allow you to create reusable components and functions that can work with multiple types. With generics, you can parameterize types, functions, and interfaces, allowing them to operate on different types without explicitly specifying them beforehand. Generics allow you to make code more flexible and reusable. ### Generic Type To define a generic type, you use angle brackets (`<>`) to specify the type parameters, for instance: ```typescript function identity(arg: T): T { return arg; } const a = identity('x'); const b = identity(123); const getLen = (data: ReadonlyArray) => data.length; const len = getLen([1, 2, 3]); ``` ### Generic Classes Generics can be applied also to classes, in this way they can work with multiple types by using type parameters. This is useful to create reusable class definitions that can operate on different data types while maintaining type safety. ```typescript class Container { private item: T; constructor(item: T) { this.item = item; } getItem(): T { return this.item; } } const numberContainer = new Container(123); console.log(numberContainer.getItem()); // 123 const stringContainer = new Container('hello'); console.log(stringContainer.getItem()); // hello ``` ### Generic Constraints Generic parameters can be constrained using the `extends` keyword followed by a type or interface that the type parameter must satisfy. In the following example T it is must containing a properly `length` in order to be valid: ```typescript const printLen = (value: T): void => { console.log(value.length); }; printLen('Hello'); // 5 printLen([1, 2, 3]); // 3 printLen({ length: 10 }); // 10 printLen(123); // Invalid ``` An interesting feature of generic introduced in version 3.4 RC is Higher order function type inference which introduced propagated generic type arguments: ```typescript declare function pipe( ab: (...args: A) => B, bc: (b: B) => C ): (...args: A) => C; declare function list(a: T): T[]; declare function box(x: V): { value: V }; const listBox = pipe(list, box); // (a: T) => { value: T[] } const boxList = pipe(box, list); // (x: V) => { value: V }[] ``` This functionality allows more easily typed safe pointfree style programming which is common in functional programming. ### Generic contextual narrowing Contextual narrowing for generics is the mechanism in TypeScript that allows the compiler to narrow down the type of a generic parameter based on the context in which it is used, it is useful when working with generic types in conditional statements: ```typescript function process(value: T): void { if (typeof value === 'string') { // Value is narrowed down to type 'string' console.log(value.length); } else if (typeof value === 'number') { // Value is narrowed down to type 'number' console.log(value.toFixed(2)); } } process('hello'); // 5 process(3.14159); // 3.14 ``` ================================================ FILE: website/src/content/docs/book/getting-started-with-typescript.md ================================================ --- title: Getting Started With TypeScript sidebar: order: 8 label: 8. Getting Started With TypeScript --- ### Installation Visual Studio Code provides excellent support for the TypeScript language but does not include the TypeScript compiler. To install the TypeScript compiler, you can use a package manager like npm or yarn: ```shell npm install typescript --save-dev ``` or ```shell yarn add typescript --dev ``` Make sure to commit the generated lockfile to ensure that every team member uses the same version of TypeScript. To run the TypeScript compiler, you can use the following commands ```shell npx tsc ``` or ```shell yarn tsc ``` It is recommended to install TypeScript project-wise rather than globally, as it provides a more predictable build process. However, for one-off occasions, you can use the following command: ```shell npx tsc ``` or installing it globally: ```shell npm install -g typescript ``` If you are using Microsoft Visual Studio, you can obtain TypeScript as a package in NuGet for your MSBuild projects. In the NuGet Package Manager Console, run the following command: ```shell Install-Package Microsoft.TypeScript.MSBuild ``` During the TypeScript installation, two executables are installed: "tsc" as the TypeScript compiler and "tsserver" as the TypeScript standalone server. The standalone server contains the compiler and language services that can be utilized by editors and IDEs to provide intelligent code completion. Additionally, there are several TypeScript-compatible transpilers available, such as Babel (via a plugin) or swc. These transpilers can be used to convert TypeScript code into other target languages or versions. ### Configuration TypeScript can be configured using the tsc CLI options or by utilizing a dedicated configuration file called tsconfig.json placed in the root of the project. To generate a tsconfig.json file prepopulated with recommended settings, you can use the following command: ```shell tsc --init ``` When executing the `tsc` command locally, TypeScript will compile the code using the configuration specified in the nearest tsconfig.json file. Here are some examples of CLI commands that run with the default settings: ```shell tsc main.ts // Compile a specific file (main.ts) to JavaScript tsc src/*.ts // Compile any .ts files under the 'src' folder to JavaScript tsc app.ts util.ts --outfile index.js // Compile two TypeScript files (app.ts and util.ts) into a single JavaScript file (index.js) ``` ### TypeScript Configuration File A tsconfig.json file is used to configure the TypeScript Compiler (tsc). Usually, it is added to the root of the project, together with the `package.json` file. Notes: * tsconfig.json accepts comments even if it is in json format. * It is advisable to use this configuration file instead of the command-line options. At the following link you can find the complete documentation and its schema: [https://www.typescriptlang.org/tsconfig](https://www.typescriptlang.org/tsconfig) [https://www.typescriptlang.org/tsconfig/](https://www.typescriptlang.org/tsconfig/) The following represents a list of the common and useful configurations: #### target The "target" property is used to specify which version of JavaScript ECMAScript version your TypeScript should emit/compile into. For modern browsers ES6 is a good option, for older browsers, ES5 is recommended. #### lib The "lib" property is used to specify which library files to include at compilation time. TypeScript automatically includes APIs for features specified in the "target" property, but it is possible to omit or pick specific libraries for particular needs. For instance, if you are working on a server project, you could exclude the "DOM" library, which is useful only in a browser environment. #### strict The "strict" property enables stronger guarantees and enhances type safety. It is advisable to always include this property in your project's tsconfig.json file. Enabling the "strict" property allows TypeScript to: * Emit code using "use strict" for each source file. * Consider "null" and "undefined" in the type checking process. * Disable the usage of the "any" type when no type annotations are present. * Raise an error on the usage of the "this" expression, which would otherwise imply the "any" type. #### module The "module" property sets the module system supported for the compiled program. During runtime, a module loader is used to locate and execute dependencies based on the specified module system. The most common module loaders used in JavaScript are Node.js CommonJS for server-side applications and RequireJS for AMD modules in browser-based web applications. TypeScript can emit code for various module systems, including UMD, System, ESNext, ES2015/ES6, and ES2020. Note: The module system should be chosen based on the target environment and the module loading mechanism available in that environment. #### moduleResolution The "moduleResolution" property specifies the module resolution strategy. Use "node" for modern TypeScript code, the "classic" strategy is used only for old versions of TypeScript (before 1.6). #### esModuleInterop The "esModuleInterop" property allows import default from CommonJS modules that did not export using the "default" property, this property provides a shim to ensure compatibility in the emitted JavaScript. After enabling this option we can use `import MyLibrary from "my-library"` instead of `import * as MyLibrary from "my-library"`. #### jsx The "jsx" property applies only to .tsx files used in ReactJS and controls how JSX constructs are compiled into JavaScript. A common option is "preserve" which will compile to a .jsx file keeping unchanged the JSX so it can be passed to different tools like Babel for further transformations. #### skipLibCheck The "skipLibCheck'' property will prevent TypeScript from type-checking the entire imported third-party packages. This property will reduce the compile time of a project. TypeScript will still check your code against the type definitions provided by these packages. #### files The "files" property indicates to the compiler a list of files that must always be included in the program. #### include The "include" property indicates to the compiler a list of files that we would like to include. This property allows glob-like patterns, such as "\*_" for any subdirectory, "_" for any file name, and "?" for optional characters. #### exclude The "exclude" property indicates to the compiler a list of files that should not be included in the compilation. This can include files such as "node_modules" or test files. Note: tsconfig.json allows comments. ### importHelpers TypeScript uses helper code when generating code for certain advanced or down-leveled JavaScript features. By default, these helpers are duplicated in files using them. The `importHelpers` option imports these helpers from the `tslib` module instead, making the JavaScript output more efficient. ### Migration to TypeScript Advice For large projects, it is recommended to adopt a gradual transition where TypeScript and JavaScript code will initially coexist. Only small projects can be migrated to TypeScript in one go. The first step of this transition is to introduce TypeScript into the build chain process. This can be done by using the "allowJs" compiler option, which permits .ts and .tsx files to coexist with existing JavaScript files. As TypeScript will fall back to a type of "any" for a variable when it cannot infer the type from JavaScript files, it is recommended to disable "noImplicitAny" in your compiler options at the beginning of the migration. The second step is to ensure that your JavaScript tests work alongside TypeScript files so that you can run tests as you convert each module. If you are using Jest, consider using `ts-jest`, which allows you to test TypeScript projects with Jest. The third step is to include type declarations for third-party libraries in your project. These declarations can be found either bundled or on DefinitelyTyped. You can search for them using [https://www.typescriptlang.org/dt/search](https://www.typescriptlang.org/dt/search) and install them using: ```shell npm install --save-dev @types/package-name ``` or ```shell yarn add --dev @types/package-name ``` The fourth step is to migrate module by module with a bottom-up approach, following your Dependency Graph starting with the leaves. The idea is to start converting Modules that do not depend on other Modules. To visualize the dependency graphs, you can use the "madge" tool. Good candidate modules for these initial conversions are utility functions and code related to external APIs or specifications. It is possible to automatically generate TypeScript type definitions from Swagger contracts, GraphQL or JSON schemas to be included in your project. When there are no specifications or official schemas available, you can generate types from raw data, such as JSON returned by a server. However, it is recommended to generate types from specifications instead of data to avoid missing edge cases. During the migration, refrain from code refactoring and focus only on adding types to your modules. The fifth step is to enable "noImplicitAny," which will enforce that all types are known and defined, providing a better TypeScript experience for your project. During the migration, you can use the `@ts-check` directive, which enables TypeScript type checking in a JavaScript file. This directive provides a loose version of type checking and can be initially used to identify issues in JavaScript files. When `@ts-check` is included in a file, TypeScript will try to deduce definitions using JSDoc-style comments. However, consider using JSDoc annotations only at a very early stage of the migration. Consider keeping the default value of `noEmitOnError` in your tsconfig.json as false. This will allow you to output JavaScript source code even if errors are reported. ================================================ FILE: website/src/content/docs/book/index-signatures.md ================================================ --- title: Index Signatures sidebar: order: 14 label: 14. Index Signatures --- In TypeScript we can use as index signature `string`, `number`, and `symbol`: ```typescript type K = { [name: string | number]: string; }; const k: K = { x: 'x', 1: 'b' }; console.log(k['x']); console.log(k[1]); console.log(k['1']); // Same result as k[1] ``` Please note that JavaScript automatically converts an index with `number` to an index with `string` so `k[1]` or `k["1"]` return the same value. ================================================ FILE: website/src/content/docs/book/infer-type-inference-in-conditional-types.md ================================================ --- title: infer Type Inference in Conditional Types sidebar: order: 41 label: 41. infer Type Inference in Conditional Types --- The `infer`keyword is used in conditional types to infer (extract) the type of a generic parameter from a type that depends on it. This allows you to write more flexible and reusable type definitions. ```typescript type ElementType = T extends (infer U)[] ? U : never; type Numbers = ElementType; // number type Strings = ElementType; // string ``` ================================================ FILE: website/src/content/docs/book/interface-and-type.md ================================================ --- title: Interface and Type sidebar: order: 48 label: 48. Interface and Type --- ### Common Syntax In TypeScript, interfaces define the structure of objects, specifying the names and types of properties or methods that an object must have. The common syntax for defining an interface in TypeScript is as follows: ```typescript interface InterfaceName { property1: Type1; // ... method1(arg1: ArgType1, arg2: ArgType2): ReturnType; // ... } ``` Similarly for type definition: ```typescript type TypeName = { property1: Type1; // ... method1(arg1: ArgType1, arg2: ArgType2): ReturnType; // ... }; ``` `interface InterfaceName` or `type TypeName`: Defines the name of the interface. `property1`: `Type1`: Specifies the properties of the interface along with their corresponding types. Multiple properties can be defined, each separated by a semicolon. `method1(arg1: ArgType1, arg2: ArgType2): ReturnType;`: Specifies the methods of the interface. Methods are defined with their names, followed by a parameter list in parentheses and the return type. Multiple methods can be defined, each separated by a semicolon. Example interface: ```typescript interface Person { name: string; age: number; greet(): void; } ``` Example of type: ```typescript type TypeName = { property1: string; method1(arg1: string, arg2: string): string; }; ``` In TypeScript, types are used to define the shape of data and enforce type checking. There are several common syntaxes for defining types in TypeScript, depending on the specific use case. Here are some examples: ### Basic Types ```typescript let myNumber: number = 123; // number type let myBoolean: boolean = true; // boolean type let myArray: string[] = ['a', 'b']; // array of strings let myTuple: [string, number] = ['a', 123]; // tuple ``` ### Objects and Interfaces ```typescript const x: { name: string; age: number } = { name: 'Simon', age: 7 }; ``` ### Union and Intersection Types ```typescript type MyType = string | number; // Union type let myUnion: MyType = 'hello'; // Can be a string myUnion = 123; // Or a number type TypeA = { name: string }; type TypeB = { age: number }; type CombinedType = TypeA & TypeB; // Intersection type let myCombined: CombinedType = { name: 'John', age: 25 }; // Object with both name and age properties ``` ================================================ FILE: website/src/content/docs/book/intersection-types.md ================================================ --- title: Intersection Types sidebar: order: 32 label: 32. Intersection Types --- An Intersection Type is a type that represents a value that has all the properties of two or more types. Intersection Types are denoted using the `&` symbol between each type. ```typescript type X = { a: string; }; type Y = { b: string; }; type J = X & Y; // Intersection const j: J = { a: 'a', b: 'b', }; ``` ================================================ FILE: website/src/content/docs/book/introduction.md ================================================ --- title: Introduction sidebar: order: 5 label: 5. Introduction --- Welcome to The Concise TypeScript Book! This guide equips you with essential knowledge and practical skills for effective TypeScript development. Discover key concepts and techniques to write clean, robust code. Whether you're a beginner or an experienced developer, this book serves as both a comprehensive guide and a handy reference for leveraging TypeScript's power in your projects. This book covers TypeScript 5.2. ================================================ FILE: website/src/content/docs/book/literal-inference.md ================================================ --- title: Literal Inference sidebar: order: 17 label: 17. Literal Inference --- Literal Inference is a feature in TypeScript that allows the type of a variable or parameter to be inferred based on its value. In the following example we can see that TypeScript considers `x` a literal type as the value cannot be changed any time later, when instead `y` is inferred as string as it can be modified any time later. ```typescript const x = 'x'; // Literal type of 'x', because this value cannot be changed let y = 'y'; // Type string, as we can change this value ``` In the following example we can see that `o.x` was inferred as a `string` (and not a literal of `a`) as TypeScript considers that the value can be changed any time later. ```typescript type X = 'a' | 'b'; let o = { x: 'a', // This is a wider string }; const fn = (x: X) => `${x}-foo`; console.log(fn(o.x)); // Argument of type 'string' is not assignable to parameter of type 'X' ``` As you can see the code throws an error when passing `o.x` to `fn` as X is a narrower type. We can solve this issue by using type assertion using `const` or the `X` type: ```typescript let o = { x: 'a' as const, }; ``` or: ```typescript let o = { x: 'a' as X, }; ``` ================================================ FILE: website/src/content/docs/book/literal-types.md ================================================ --- title: Literal Types sidebar: order: 16 label: 16. Literal Types --- A Literal Type is a single element set from a collective type, it defines a very exact value that is a JavaScript primitive. Literal Types in TypeScript are numbers, strings, and booleans. Example of literals: ```typescript const a = 'a'; // String literal type const b = 1; // Numeric literal type const c = true; // Boolean literal type ``` String, Numeric, and Boolean Literal Types are used in unions, type guards, and type aliases. In the following example, you can see a union type alias. `O` consists of only the specified values, no other string is valid: ```typescript type O = 'a' | 'b' | 'c'; ``` ================================================ FILE: website/src/content/docs/book/mapped-type-modifiers.md ================================================ --- title: Mapped Type Modifiers sidebar: order: 38 label: 38. Mapped Type Modifiers --- Mapped Type Modifiers in TypeScript enable the transformation of properties within an existing type: * `readonly` or `+readonly`: This renders a property in the mapped type as read-only. * `-readonly`: This allows a property in the mapped type to be mutable. * `?`: This designates a property in the mapped type as optional. Examples: ```typescript type ReadOnly = { readonly [P in keyof T]: T[P] }; // All properties marked as read-only type Mutable = { -readonly [P in keyof T]: T[P] }; // All properties marked as mutable type MyPartial = { [P in keyof T]?: T[P] }; // All properties marked as optional ``` ================================================ FILE: website/src/content/docs/book/mapped-types.md ================================================ --- title: Mapped Types sidebar: order: 37 label: 37. Mapped Types --- Mapped Types in TypeScript allow you to create new types based on an existing type by transforming each property using a mapping function. By mapping existing types, you can create new types that represent the same information in a different format. To create a mapped type, you access the properties of an existing type using the `keyof` operator and then alter them to produce a new type. In the following example: ```typescript type MyMappedType = { [P in keyof T]: T[P][]; }; type MyType = { foo: string; bar: number; }; type MyNewType = MyMappedType; const x: MyNewType = { foo: ['hello', 'world'], bar: [1, 2, 3], }; ``` we define MyMappedType to map over T's properties, creating a new type with each property as an array of its original type. Using this, we create MyNewType to represent the same info as MyType, but with each property as an array. ================================================ FILE: website/src/content/docs/book/merging-and-extension.md ================================================ --- title: Merging and Extension sidebar: order: 52 label: 52. Merging and Extension --- Merging and extension refer to two different concepts related to working with types and interfaces. Merging allows you to combine multiple declarations of the same name into a single definition, for example, when you define an interface with the same name multiple times: ```typescript interface X { a: string; } interface X { b: number; } const person: X = { a: 'a', b: 7, }; ``` Extension refers to the ability to extend or inherit from existing types or interfaces to create new ones. It is a mechanism to add additional properties or methods to an existing type without modifying its original definition. Example: ```typescript interface Animal { name: string; eat(): void; } interface Bird extends Animal { sing(): void; } const dog: Bird = { name: 'Bird 1', eat() { console.log('Eating'); }, sing() { console.log('Singing'); }, }; ``` ================================================ FILE: website/src/content/docs/book/named-tuple-type-labeled.md ================================================ --- title: Named Tuple Type (Labeled) sidebar: order: 29 label: 29. Named Tuple Type (Labeled) --- Tuple types can include optional labels or names for each element. These labels are for readability and tooling assistance, and do not affect the operations you can perform with them. ```typescript type T = string; type Tuple1 = [T, T]; type Tuple2 = [a: T, b: T]; type Tuple3 = [a: T, T]; // Named Tuple plus Anonymous Tuple ``` ================================================ FILE: website/src/content/docs/book/namespacing.md ================================================ --- title: Namespacing sidebar: order: 57 label: 57. Namespacing --- In TypeScript, namespaces are used to organize code into logical containers, preventing naming collisions and providing a way to group related code together. The usage of the `export` keywords allows access to the namespace in "outside" modules. ```typescript export namespace MyNamespace { export interface MyInterface1 { prop1: boolean; } export interface MyInterface2 { prop2: string; } } const a: MyNamespace.MyInterface1 = { prop1: true, }; ``` ================================================ FILE: website/src/content/docs/book/narrowing.md ================================================ --- title: Narrowing sidebar: order: 20 label: 20. Narrowing --- TypeScript narrowing is the process of refining the type of a variable within a conditional block. This is useful when working with union types, where a variable can have more than one type. TypeScript recognizes several ways to narrow the type: ### typeof type guards The typeof type guard is one specific type guard in TypeScript that checks the type of a variable based on its built-in JavaScript type. ```typescript const fn = (x: number | string) => { if (typeof x === 'number') { return x + 1; // x is number } return -1; }; ``` ### Truthiness narrowing Truthiness narrowing in TypeScript works by checking whether a variable is truthy or falsy to narrow its type accordingly. ```typescript const toUpperCase = (name: string | null) => { if (name) { return name.toUpperCase(); } else { return null; } }; ``` ### Equality narrowing Equality narrowing in TypeScript works by checking whether a variable is equal to a specific value or not, to narrow its type accordingly. It is used in conjunction with `switch` statements and equality operators such as `===`, `!==`, `==`, and `!=` to narrow down types. ```typescript const checkStatus = (status: 'success' | 'error') => { switch (status) { case 'success': return true; case 'error': return null; } }; ``` ### In Operator narrowing The `in` Operator narrowing in TypeScript is a way to narrow the type of a variable based on whether a property exists within the variable's type. ```typescript type Dog = { name: string; breed: string; }; type Cat = { name: string; likesCream: boolean; }; const getAnimalType = (pet: Dog | Cat) => { if ('breed' in pet) { return 'dog'; } else { return 'cat'; } }; ``` ### instanceof narrowing The `instanceof` operator narrowing in TypeScript is a way to narrow the type of a variable based on its constructor function, by checking if an object is an instance of a certain class or interface. ```typescript class Square { constructor(public width: number) {} } class Rectangle { constructor( public width: number, public height: number ) {} } function area(shape: Square | Rectangle) { if (shape instanceof Square) { return shape.width * shape.width; } else { return shape.width * shape.height; } } const square = new Square(5); const rectangle = new Rectangle(5, 10); console.log(area(square)); // 25 console.log(area(rectangle)); // 50 ``` ================================================ FILE: website/src/content/docs/book/never-type.md ================================================ --- title: Never type sidebar: order: 47 label: 47. Never type --- The `never` type represents values that never occur. It is used to denote functions or expressions that never return or throw an error. For instance an infinite loop: ```typescript const infiniteLoop = (): never => { while (true) { // do something } }; ``` Throwing an error: ```typescript const throwError = (message: string): never => { throw new Error(message); }; ``` The `never` type is useful in ensuring type safety and catching potential errors in your code. It helps TypeScript analyze and infer more precise types when used in combination with other types and control flow statements, for instance: ```typescript type Direction = 'up' | 'down'; const move = (direction: Direction): void => { switch (direction) { case 'up': // move up break; case 'down': // move down break; default: const exhaustiveCheck: never = direction; throw new Error(`Unhandled direction: ${exhaustiveCheck}`); } }; ``` ================================================ FILE: website/src/content/docs/book/object-types.md ================================================ --- title: Object Types sidebar: order: 27 label: 27. Object Types --- In TypeScript, object types describe the shape of an object. They specify the names and types of the object's properties, as well as whether those properties are required or optional. In TypeScript, you can define object types in two primary ways: Interface which defines the shape of an object by specifying the names, types, and optionality of its properties. ```typescript interface User { name: string; age: number; email?: string; } ``` Type alias, similar to an interface, defines the shape of an object. However, it can also create a new custom type that is based on an existing type or a combination of existing types. This includes defining union types, intersection types, and other complex types. ```typescript type Point = { x: number; y: number; }; ``` It also possible to define a type anonymously: ```typescript const sum = (x: { a: number; b: number }) => x.a + x.b; console.log(sum({ a: 5, b: 1 })); ``` ================================================ FILE: website/src/content/docs/book/optional-properties.md ================================================ --- title: Optional Properties sidebar: order: 12 label: 12. Optional Properties --- An object can specify Optional Properties by adding a question mark `?` to the end of the property name: ```typescript type X = { a: number; b?: number; // Optional }; ``` It is possible to specify a default value when a property is optional: ```typescript type X = { a: number; b?: number; }; const x = ({ a, b = 100 }: X) => a + b; ``` ================================================ FILE: website/src/content/docs/book/others.md ================================================ --- title: Others sidebar: order: 61 label: 61. Others --- ### Errors and Exception Handling TypeScript allows you to catch and handle errors using standard JavaScript error handling mechanisms: Try-Catch-Finally Blocks: ```typescript try { // Code that might throw an error } catch (error) { // Handle the error } finally { // Code that always executes, finally is optional } ``` You can also handle different types of error: ```typescript try { // Code that might throw different types of errors } catch (error) { if (error instanceof TypeError) { // Handle TypeError } else if (error instanceof RangeError) { // Handle RangeError } else { // Handle other errors } } ``` Custom Error Types: It is possible to specify more specific error by extending on the Error `class`: ```typescript class CustomError extends Error { constructor(message: string) { super(message); this.name = 'CustomError'; } } throw new CustomError('This is a custom error.'); ``` ### Mixin classes Mixin classes allow you to combine and compose behavior from multiple classes into a single class. They provide a way to reuse and extend functionality without the need for deep inheritance chains. ```typescript abstract class Identifiable { name: string = ''; logId() { console.log('id:', this.name); } } abstract class Selectable { selected: boolean = false; select() { this.selected = true; console.log('Select'); } deselect() { this.selected = false; console.log('Deselect'); } } class MyClass { constructor() {} } // Extend MyClass to include the behavior of Identifiable and Selectable interface MyClass extends Identifiable, Selectable {} // Function to apply mixins to a class function applyMixins(source: any, baseCtors: any[]) { baseCtors.forEach(baseCtor => { Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => { let descriptor = Object.getOwnPropertyDescriptor( baseCtor.prototype, name ); if (descriptor) { Object.defineProperty(source.prototype, name, descriptor); } }); }); } // Apply the mixins to MyClass applyMixins(MyClass, [Identifiable, Selectable]); let o = new MyClass(); o.name = 'abc'; o.logId(); o.select(); ``` ### Asynchronous Language Features As TypeScript is a superset of JavaScript, it has built-in asynchronous language features of JavaScript as: Promises: Promises are a way to handle asynchronous operations and their results using methods like `.then()` and `.catch()` to handle success and error conditions. To learn more: [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) Async/await: Async/await keywords are a way to provide a more synchronous-looking syntax for working with Promises. The `async` keyword is used to define an asynchronous function, and the `await` keyword is used within an async function to pause execution until a Promise is resolved or rejected. To learn more: [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function) [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await) The following API are well supported in TypeScript: Fetch API: [https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) Web Workers: [https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API) Shared Workers: [https://developer.mozilla.org/en-US/docs/Web/API/SharedWorker](https://developer.mozilla.org/en-US/docs/Web/API/SharedWorker) WebSocket: [https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API) ### Iterators and Generators Both Iterators and Generators are well supported in TypeScript. Iterators are objects that implement the iterator protocol, providing a way to access elements of a collection or sequence one by one. It is a structure that contains a pointer to the next element in the iteration. They have a `next()` method that returns the next value in the sequence along with a boolean indicating if the sequence is `done`. ```typescript class NumberIterator implements Iterable { private current: number; constructor( private start: number, private end: number ) { this.current = start; } public next(): IteratorResult { if (this.current <= this.end) { const value = this.current; this.current++; return { value, done: false }; } else { return { value: undefined, done: true }; } } [Symbol.iterator](): Iterator { return this; } } const iterator = new NumberIterator(1, 3); for (const num of iterator) { console.log(num); } ``` Generators are special functions defined using the `function*` syntax that simplifies the creation of iterators. They use the `yield` keyword to define the sequence of values and automatically pause and resume execution when values are requested. Generators make it easier to create iterators and are especially useful for working with large or infinite sequences. Example: ```typescript function* numberGenerator(start: number, end: number): Generator { for (let i = start; i <= end; i++) { yield i; } } const generator = numberGenerator(1, 5); for (const num of generator) { console.log(num); } ``` TypeScript also supports async iterators and async Generators. To learn more: [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Generator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Generator) [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Iterator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Iterator) ### TsDocs JSDoc Reference When working with a JavaScript code base, it is possible to help TypeScript to infer the right Type by using JSDoc comments with additional annotation to provide type information. Example: ```typescript /** * Computes the power of a given number * @constructor * @param {number} base – The base value of the expression * @param {number} exponent – The exponent value of the expression */ function power(base: number, exponent: number) { return Math.pow(base, exponent); } power(10, 2); // function power(base: number, exponent: number): number ``` Full documentation is provided to this link: [https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html](https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html) From version 3.7 it is possible to generate .d.ts type definitions from JavaScript JSDoc syntax. More information can be found here: [https://www.typescriptlang.org/docs/handbook/declaration-files/dts-from-js.html](https://www.typescriptlang.org/docs/handbook/declaration-files/dts-from-js.html) ### @types Packages under the @types organization are special package naming conventions used to provide type definitions for existing JavaScript libraries or modules. For instance using: ```shell npm install --save-dev @types/lodash ``` Will install the type definitions of `lodash` in your current project. To contribute to the type definitions of @types package, please submit a pull request to [https://github.com/DefinitelyTyped/DefinitelyTyped](https://github.com/DefinitelyTyped/DefinitelyTyped). ### JSX JSX (JavaScript XML) is an extension to the JavaScript language syntax that allows you to write HTML-like code within your JavaScript or TypeScript files. It is commonly used in React to define the HTML structure. TypeScript extends the capabilities of JSX by providing type checking and static analysis. To use JSX you need to set the `jsx` compiler option in your `tsconfig.json` file. Two common configuration options: * "preserve": emit .jsx files with the JSX unchanged. This option tells TypeScript to keep the JSX syntax as-is and not transform it during the compilation process. You can use this option if you have a separate tool, like Babel, that handles the transformation. * "react": enables TypeScript's built-in JSX transformation. React.createElement will be used. All options are available here: [https://www.typescriptlang.org/tsconfig#jsx](https://www.typescriptlang.org/tsconfig#jsx) ### ES6 Modules TypeScript does support ES6 (ECMAScript 2015) and many subsequent versions. This means you can use ES6 syntax, such as arrow functions, template literals, classes, modules, destructuring, and more. To enable ES6 features in your project, you can specify the `target` property in the tsconfig.json. A configuration example: ```json { "compilerOptions": { "target": "es6", "module": "es6", "moduleResolution": "node", "sourceMap": true, "outDir": "dist" }, "include": ["src"] } ``` ### ES7 Exponentiation Operator The exponentiation (`**`) operator computes the value obtained by raising the first operand to the power of the second operand. It functions similarly to `Math.pow()`, but with the added capability of accepting BigInts as operands. TypeScript fully supports this operator using as `target` in your tsconfig.json file `es2016` or larger version. ```typescript console.log(2 ** (2 ** 2)); // 16 ``` ### The for-await-of Statement This is a JavaScript feature fully supported in TypeScript which allows you to iterate over asynchronous iterable objects from target version es2018. ```typescript async function* asyncNumbers(): AsyncIterableIterator { yield Promise.resolve(1); yield Promise.resolve(2); yield Promise.resolve(3); } (async () => { for await (const num of asyncNumbers()) { console.log(num); } })(); ``` ### New target meta-property You can use in TypeScript the `new.target` meta-property which enables you to determine if a function or constructor was invoked using the new operator. It allows you to detect whether an object was created as a result of a constructor call. ```typescript class Parent { constructor() { console.log(new.target); // Logs the constructor function used to create an instance } } class Child extends Parent { constructor() { super(); } } const parentX = new Parent(); // [Function: Parent] const child = new Child(); // [Function: Child] ``` ### Dynamic Import Expressions It is possible to conditionally load modules or lazy load them on-demand using the ECMAScript proposal for dynamic import which is supported in TypeScript. The syntax for dynamic import expressions in TypeScript is as follows: ```typescript async function renderWidget() { const container = document.getElementById('widget'); if (container !== null) { const widget = await import('./widget'); // Dynamic import widget.render(container); } } renderWidget(); ``` ### "tsc –watch" This command starts a TypeScript compiler with `--watch` parameter, with the ability to automatically recompile TypeScript files whenever they are modified. ```shell tsc --watch ``` Starting from TypeScript version 4.9, file monitoring primarily relies on file system events, automatically resorting to polling if an event-based watcher cannot be established. ### Non-null Assertion Operator The Non-null Assertion Operator (Postfix !) also called Definite Assignment Assertions is a TypeScript feature that allows you to assert that a variable or property is not null or undefined, even if TypeScript's static type analysis suggests that it might be. With this feature it is possible to remove any explicit checking. ```typescript type Person = { name: string; }; const printName = (person?: Person) => { console.log(`Name is ${person!.name}`); }; ``` ### Defaulted declarations Defaulted declarations are used when a variable or parameter is assigned a default value. This means that if no value is provided for that variable or parameter, the default value will be used instead. ```typescript function greet(name: string = 'Anonymous'): void { console.log(`Hello, ${name}!`); } greet(); // Hello, Anonymous! greet('John'); // Hello, John! ``` ### Optional Chaining The optional chaining operator `?.` works like the regular dot operator (`.`) for accessing properties or methods. However, it gracefully handles null or undefined values by terminating the expression and returning `undefined`, instead of throwing an error. ```typescript type Person = { name: string; age?: number; address?: { street?: string; city?: string; }; }; const person: Person = { name: 'John', }; console.log(person.address?.city); // undefined ``` ### Nullish coalescing operator The nullish coalescing operator `??` returns the right-hand side value if the left-hand side is `null` or `undefined`; otherwise, it returns the left-hand side value. ```typescript const foo = null ?? 'foo'; console.log(foo); // foo const baz = 1 ?? 'baz'; const baz2 = 0 ?? 'baz'; console.log(baz); // 1 console.log(baz2); // 0 ``` ### Template Literal Types Template Literal Types allow to manipulate string value at type level and generate new string types based on existing ones. They are useful to create more expressive and precise types from string-based operations. ```typescript type Department = 'engineering' | 'hr'; type Language = 'english' | 'spanish'; type Id = `${Department}-${Language}-id`; // "engineering-english-id" | "engineering-spanish-id" | "hr-english-id" | "hr-spanish-id" ``` ### Function overloading Function overloading allows you to define multiple function signatures for the same function name, each with different parameter types and return type. When you call an overloaded function, TypeScript uses the provided arguments to determine the correct function signature: ```typescript function makeGreeting(name: string): string; function makeGreeting(names: string[]): string[]; function makeGreeting(person: unknown): unknown { if (typeof person === 'string') { return `Hi ${person}!`; } else if (Array.isArray(person)) { return person.map(name => `Hi, ${name}!`); } throw new Error('Unable to greet'); } makeGreeting('Simon'); makeGreeting(['Simone', 'John']); ``` ### Recursive Types A Recursive Type is a type that can refer to itself. This is useful for defining data structures that have a hierarchical or recursive structure (potentially infinite nesting), such as linked lists, trees, and graphs. ```typescript type ListNode = { data: T; next: ListNode | undefined; }; ``` ### Recursive Conditional Types It is possible to define complex type relationships using logic and recursion in TypeScript. Let’s break it down in simple terms: Conditional Types: allows you to define types based on boolean conditions: ```typescript type CheckNumber = T extends number ? 'Number' : 'Not a number'; type A = CheckNumber<123>; // 'Number' type B = CheckNumber<'abc'>; // 'Not a number' ``` Recursion: means a type definition that refers to itself within its own definition: ```typescript type Json = string | number | boolean | null | Json[] | { [key: string]: Json }; const data: Json = { prop1: true, prop2: 'prop2', prop3: { prop4: [], }, }; ``` Recursive Conditional Types combine both conditional logic and recursion. It means that a type definition can depend on itself through conditional logic, creating complex and flexible type relationships. ```typescript type Flatten = T extends Array ? Flatten : T; type NestedArray = [1, [2, [3, 4], 5], 6]; type FlattenedArray = Flatten; // 2 | 3 | 4 | 5 | 1 | 6 ``` ### ECMAScript Module Support in Node Node.js added support for ECMAScript Modules starting from version 15.3.0, and TypeScript has had ECMAScript Module Support for Node.js since version 4.7. This support can be enabled by using the `module` property with the value `nodenext` in the tsconfig.json file. Here's an example: ```json { "compilerOptions": { "module": "nodenext", "outDir": "./lib", "declaration": true } } ``` Node.js supports two file extensions for modules: `.mjs` for ES modules and `.cjs` for CommonJS modules. The equivalent file extensions in TypeScript are `.mts` for ES modules and `.cts` for CommonJS modules. When the TypeScript compiler transpiles these files to JavaScript, it will create `.mjs` and `.cjs` files. If you want to use ES modules in your project, you can set the `type` property to "module" in your package.json file. This instructs Node.js to treat the project as an ES module project. Additionally, TypeScript also supports type declarations in .d.ts files. These declaration files provide type information for libraries or modules written in TypeScript, allowing other developers to utilize them with TypeScript's type checking and auto-completion features. ### Assertion Functions In TypeScript, assertion functions are functions that indicate the verification of a specific condition based on their return value. In their simplest form, an assert function examines a provided predicate and raises an error when the predicate evaluates to false. ```typescript function isNumber(value: unknown): asserts value is number { if (typeof value !== 'number') { throw new Error('Not a number'); } } ``` Or can be declared as function expression: ```typescript type AssertIsNumber = (value: unknown) => asserts value is number; const isNumber: AssertIsNumber = value => { if (typeof value !== 'number') { throw new Error('Not a number'); } }; ``` Assertion functions share similarities with type guards. Type guards were initially introduced to perform runtime checks and ensure the type of a value within a specific scope. Specifically, a type guard is a function that evaluates a type predicate and returns a boolean value indicating whether the predicate is true or false. This differs slightly from assertion functions,where the intention is to throw an error rather than returning false when the predicate is not satisfied. Example of type guard: ```typescript const isNumber = (value: unknown): value is number => typeof value === 'number'; ``` ### Variadic Tuple Types Variadic Tuple Types are a features introduces in TypeScript version 4.0, let’s start to learn them by revise what is a tuple: A tuple type is an array which has a defined length, and were the type of each element is known: ```typescript type Student = [string, number]; const [name, age]: Student = ['Simone', 20]; ``` The term "variadic" means indefinite arity (accept a variable number of arguments). A variadic tuple is a tuple type which has all the property as before but the exact shape is not defined yet: ```typescript type Bar = [boolean, ...T, number]; type A = Bar<[boolean]>; // [boolean, boolean, number] type B = Bar<['a', 'b']>; // [boolean, 'a', 'b', number] type C = Bar<[]>; // [boolean, number] ``` In the previous code we can see that the tuple shape is defined by the `T` generic passed in. Variadic tuples can accept multiple generics make them very flexible: ```typescript type Bar = [...T, boolean, ...G]; type A = Bar<[number], [string]>; // [number, boolean, string] type B = Bar<['a', 'b'], [boolean]>; // ["a", "b", boolean, boolean] ``` With the new variadic tuples we can use: * The spreads in tuple type syntax can now be generic, so we can represent higher-order operation on tuples and arrays even when we do not know the actual types we are operating over. * The rest elements can occur anywhere in a tuple. Example: ```typescript type Items = readonly unknown[]; function concat( arr1: T, arr2: U ): [...T, ...U] { return [...arr1, ...arr2]; } concat([1, 2, 3], ['4', '5', '6']); // [1, 2, 3, "4", "5", "6"] ``` ### Boxed types Boxed types refer to the wrapper objects that are used to represent primitive types as objects. These wrapper objects provide additional functionality and methods that are not available directly on the primitive values. When you access a method like `charAt` or `normalize` on a `string` primitive, JavaScript wraps it in a `String` object, calls the method, and then throws the object away. Demonstration: ```typescript const originalNormalize = String.prototype.normalize; String.prototype.normalize = function () { console.log(this, typeof this); return originalNormalize.call(this); }; console.log('\u0041'.normalize()); ``` TypeScript represents this differentiation by providing separate types for the primitives and their corresponding object wrappers: * string => String * number => Number * boolean => Boolean * symbol => Symbol * bigint => BigInt The boxed types are usually not needed. Avoid using boxed types and instead use type for the primitives, for instance `string` instead of `String`. ### Covariance and Contravariance in TypeScript Covariance and Contravariance are used to describe how relationships work when dealing with inheritance or assignment of types. Covariance means that a type relationship preserves the direction of inheritance or assignment, so if a type A is a subtype of type B, then an array of type A is also considered a subtype of an array of type B. The important thing to note here is that the subtype relationship is maintained this means that Covariance accept subtype but doesn't accept supertype. Contravariance means that a type relationship reverses the direction of inheritance or assignment, so if a type A is a subtype of type B, then an array of type B is considered a subtype of an array of type A. The subtype relationship is reversed this means that Contravariance accept supertype but doesn't accept subtype. Notes: Bivariance means accept both supertype & subtype. Example: Let's say we have a space for all animals and a separate space just for dogs. In Covariance, you can put all the dogs in the animals space because dogs are a type of animal. But you cannot put all the animals in the dog space because there might be other animals mixed in. In Contravariance, you cannot put all the animals in the dogs space because the animals space might contain other animals as well. However, you can put all the dogs in the animal space because all dogs are also animals. ```typescript // Covariance example class Animal { name: string; constructor(name: string) { this.name = name; } } class Dog extends Animal { breed: string; constructor(name: string, breed: string) { super(name); this.breed = breed; } } let animals: Animal[] = []; let dogs: Dog[] = []; // Covariance allows assigning subtype (Dog) array to supertype (Animal) array animals = dogs; dogs = animals; // Invalid: Type 'Animal[]' is not assignable to type 'Dog[]' // Contravariance example type Feed = (animal: T) => void; let feedAnimal: Feed = (animal: Animal) => { console.log(`Animal name: ${animal.name}`); }; let feedDog: Feed = (dog: Dog) => { console.log(`Dog name: ${dog.name}, Breed: ${dog.breed}`); }; // Contravariance allows assigning supertype (Animal) callback to subtype (Dog) callback feedDog = feedAnimal; feedAnimal = feedDog; // Invalid: Type 'Feed' is not assignable to type 'Feed'. ``` In TypeScript, type relationships for arrays are covariant, while type relationships for function parameters are contravariant. This means that TypeScript exhibits both covariance and contravariance, depending on the context. #### Optional Variance Annotations for Type Parameters As of TypeScript 4.7.0, we can use the `out` and `in` keywords to be specific about Variance annotation. For Covariant, use the `out` keyword: ```typescript type AnimalCallback = () => T; // T is Covariant here ``` And for Contravariant, use the `in` keyword: ```typescript type AnimalCallback = (value: T) => void; // T is Contravariance here ``` ### Template String Pattern Index Signatures Template string pattern index signatures allow us to define flexible index signatures using template string patterns. This feature enables us to create objects that can be indexed with specific patterns of string keys, providing more control and specificity when accessing and manipulating properties. TypeScript from version 4.4 allows index signatures for symbols and template string patterns. ```typescript const uniqueSymbol = Symbol('description'); type MyKeys = `key-${string}`; type MyObject = { [uniqueSymbol]: string; [key: MyKeys]: number; }; const obj: MyObject = { [uniqueSymbol]: 'Unique symbol key', 'key-a': 123, 'key-b': 456, }; console.log(obj[uniqueSymbol]); // Unique symbol key console.log(obj['key-a']); // 123 console.log(obj['key-b']); // 456 ``` ### The satisfies Operator The `satisfies` allows you to check if a given type satisfies a specific interface or condition. In other words, it ensures that a type has all the required properties and methods of a specific interface. It is a way to ensure a variable fits into a definition of a type. Here is an example: ```typescript type Columns = 'name' | 'nickName' | 'attributes'; type User = Record; // Type Annotation using `User` const user: User = { name: 'Simone', nickName: undefined, attributes: ['dev', 'admin'], }; // In the following lines, TypeScript won't be able to infer properly user.attributes?.map(console.log); // Property 'map' does not exist on type 'string | string[]'. Property 'map' does not exist on type 'string'. user.nickName; // string | string[] | undefined // Type assertion using `as` const user2 = { name: 'Simon', nickName: undefined, attributes: ['dev', 'admin'], } as User; // Here too, TypeScript won't be able to infer properly user2.attributes?.map(console.log); // Property 'map' does not exist on type 'string | string[]'. Property 'map' does not exist on type 'string'. user2.nickName; // string | string[] | undefined // Using `satisfies` operators we can properly infer the types now const user3 = { name: 'Simon', nickName: undefined, attributes: ['dev', 'admin'], } satisfies User; user3.attributes?.map(console.log); // TypeScript infers correctly: string[] user3.nickName; // TypeScript infers correctly: undefined ``` ### Type-Only Imports and Export Type-Only Imports and Export allows you to import or export types without importing or exporting the values or functions associated with those types. This can be useful for reducing the size of your bundle. To use type-only imports, you can use the `import type` keyword. TypeScript permits using both declaration and implementation file extensions (.ts, .mts, .cts, and .tsx) in type-only imports, regardless of `allowImportingTsExtensions` settings. For example: ```typescript import type { House } from './house.ts'; ``` The following are supported forms: ```typescript import type T from './mod'; import type { A, B } from './mod'; import type * as Types from './mod'; export type { T }; export type { T } from './mod'; ``` ### using declaration and Explicit Resource Management A `using` declaration is a block-scoped, immutable binding, similar to `const`, used for managing disposable resources. When initialized with a value, the `Symbol.dispose` method of that value is recorded and subsequently executed upon exiting the enclosing block scope. This is based on ECMAScript's Resource Management feature, which is useful for performing essential cleanup tasks after object creation, such as closing connections, deleting files, and releasing memory. Notes: * Due to its recent introduction in TypeScript version 5.2, most runtimes lack native support. You'll need polyfills for: `Symbol.dispose`, `Symbol.asyncDispose`, `DisposableStack`, `AsyncDisposableStack`, `SuppressedError`. * Additionally, you will need to configure your tsconfig.json as follows: ```json { "compilerOptions": { "target": "es2022", "lib": ["es2022", "esnext.disposable", "dom"] } } ``` Example: ```typescript //@ts-ignore Symbol.dispose ??= Symbol('Symbol.dispose'); // Simple polify const doWork = (): Disposable => { return { [Symbol.dispose]: () => { console.log('disposed'); }, }; }; console.log(1); { using work = doWork(); // Resource is declared console.log(2); } // Resource is disposed (e.g., `work[Symbol.dispose]()` is evaluated) console.log(3); ``` The code will log: ```shell 1 2 disposed 3 ``` A resource eligible for disposal must adhere to the `Disposable` interface: ```typescript // lib.esnext.disposable.d.ts interface Disposable { [Symbol.dispose](): void; } ``` The `using` declarations record resource disposal operations in a stack, ensuring they are disposed in reverse order of declaration: ```typescript { using j = getA(), y = getB(); using k = getC(); } // disposes `C`, then `B`, then `A`. ``` Resources are guaranteed to be disposed, even if subsequent code or exceptions occur. This may lead to disposal potentially throwing an exception, possibly suppressing another. To retain information on suppressed errors, a new native exception, `SuppressedError`, is introduced. #### await using declaration An `await using` declaration handles an asynchronously disposable resource. The value must have a `Symbol.asyncDispose` method, which will be awaited at the block's end. ```typescript async function doWorkAsync() { await using work = doWorkAsync(); // Resource is declared } // Resource is disposed (e.g., `await work[Symbol.asyncDispose]()` is evaluated) ``` For an asynchronously disposable resource, it must adhere to either the `Disposable` or `AsyncDisposable` interface: ```typescript // lib.esnext.disposable.d.ts interface AsyncDisposable { [Symbol.asyncDispose](): Promise; } ``` ```typescript //@ts-ignore Symbol.asyncDispose ??= Symbol('Symbol.asyncDispose'); // Simple polify class DatabaseConnection implements AsyncDisposable { // A method that is called when the object is disposed asynchronously [Symbol.asyncDispose]() { // Close the connection and return a promise return this.close(); } async close() { console.log('Closing the connection...'); await new Promise(resolve => setTimeout(resolve, 1000)); console.log('Connection closed.'); } } async function doWork() { // Create a new connection and dispose it asynchronously when it goes out of scope await using connection = new DatabaseConnection(); // Resource is declared console.log('Doing some work...'); } // Resource is disposed (e.g., `await connection[Symbol.asyncDispose]()` is evaluated) doWork(); ``` The code logs: ```shell Doing some work... Closing the connection... Connection closed. ``` The `using` and `await using` declarations are allowed in Statements: `for`, `for-in`, `for-of`, `for-await-of`, `switch`. ### Import Attributes TypeScript 5.3's Import Attributes (labels for imports) tell the runtime how to handle modules (JSON, etc.). This improves security by ensuring clear imports and aligns with Content Security Policy (CSP) for safer resource loading. TypeScript ensures they are valid but lets the runtime handle their interpretation for specific module handling. Example: ```typescript import config from './config.json' with { type: 'json' }; ``` with dynamic import: ```typescript const config = import('./config.json', { with: { type: 'json' } }); ``` ================================================ FILE: website/src/content/docs/book/overloads.md ================================================ --- title: Overloads sidebar: order: 51 label: 51. Overloads --- Function overloads in TypeScript allow you to define multiple function signatures for a single function name, enabling you to define functions that can be called in multiple ways. Here's an example: ```typescript // Overloads function sayHi(name: string): string; function sayHi(names: string[]): string[]; // Implementation function sayHi(name: unknown): unknown { if (typeof name === 'string') { return `Hi, ${name}!`; } else if (Array.isArray(name)) { return name.map(name => `Hi, ${name}!`); } throw new Error('Invalid value'); } sayHi('xx'); // Valid sayHi(['aa', 'bb']); // Valid ``` Here's another example of using function overloads within a `class`: ```typescript class Greeter { message: string; constructor(message: string) { this.message = message; } // overload sayHi(name: string): string; sayHi(names: string[]): ReadonlyArray; // implementation sayHi(name: unknown): unknown { if (typeof name === 'string') { return `${this.message}, ${name}!`; } else if (Array.isArray(name)) { return name.map(name => `${this.message}, ${name}!`); } throw new Error('value is invalid'); } } console.log(new Greeter('Hello').sayHi('Simon')); ``` ================================================ FILE: website/src/content/docs/book/predefined-conditional-types.md ================================================ --- title: Predefined Conditional Types sidebar: order: 42 label: 42. Predefined Conditional Types --- In TypeScript, Predefined Conditional Types are built-in conditional types provided by the language. They are designed to perform common type transformations based on the characteristics of a given type. `Exclude`: This type removes all the types from Type that are assignable to ExcludedType. `Extract`: This type extracts all the types from Union that are assignable to Type. `NonNullable`: This type removes null and undefined from Type. `ReturnType`: This type extracts the return type of a function Type. `Parameters`: This type extracts the parameter types of a function Type. `Required`: This type makes all properties in Type required. `Partial`: This type makes all properties in Type optional. `Readonly`: This type makes all properties in Type readonly. ================================================ FILE: website/src/content/docs/book/primitive-types.md ================================================ --- title: Primitive Types sidebar: order: 10 label: 10. Primitive Types --- TypeScript supports 7 primitive types. A primitive data type refers to a type that is not an object and does not have any methods associated with it. In TypeScript, all primitive types are immutable, meaning their values cannot be changed once they are assigned. ### string The `string` primitive type stores textual data, and the value is always double or single-quoted. ```typescript const x: string = 'x'; const y: string = 'y'; ``` Strings can span multiple lines if surrounded by the backtick (`) character: ```typescript let sentence: string = `xxx, yyy`; ``` ### boolean The `boolean` data type in TypeScript stores a binary value, either `true` or `false`. ```typescript const isReady: boolean = true; ``` ### number A `number` data type in TypeScript is represented with a 64-bit floating point value. A `number` type can represent integers and fractions. TypeScript also supports hexadecimal, binary, and octal, for instance: ```typescript const decimal: number = 10; const hexadecimal: number = 0xa00d; // Hexadecimal starts with 0x const binary: number = 0b1010; // Binary starts with 0b const octal: number = 0o633; // Octal starts with 0o ``` ### bigInt A `bigInt` represents numeric values that are very large (253 – 1) and cannot be represented with a `number`. A `bigInt` can be created by calling the built-in function `BigInt()` or by adding `n` to the end of any integer numeric literal: ```typescript const x: bigint = BigInt(9007199254740991); const y: bigint = 9007199254740991n; ``` Notes: * `bigInt` values cannot be mixed with `number` and cannot be used with built-in `Math`, they must be coerced to the same type. * `bigInt` values are available only if target configuration is ES2020 or higher. ### Symbol Symbols are unique identifiers that can be used as property keys in objects to prevent naming conflicts. ```typescript type Obj = { [sym: symbol]: number; }; const a = Symbol('a'); const b = Symbol('b'); let obj: Obj = {}; obj[a] = 123; obj[b] = 456; console.log(obj[a]); // 123 console.log(obj[b]); // 456 ``` ### null and undefined `null` and `undefined` types both represent no value or the absence of any value. The `undefined` type means the value is not assigned or initialized or indicates an unintentional absence of value. The `null` type means that we know that the field does not have a value, so value is unavailable, it indicates an intentional absence of value. ### Array An `array` is a data type that can store multiple values of the same type or not. It can be defined using the following syntax: ```typescript const x: string[] = ['a', 'b']; const y: Array = ['a', 'b']; const j: Array = ['a', 1, 'b', 2]; // Union ``` TypeScript supports readonly arrays using the following syntax: ```typescript const x: readonly string[] = ['a', 'b']; // Readonly modifier const y: ReadonlyArray = ['a', 'b']; const j: ReadonlyArray = ['a', 1, 'b', 2]; j.push('x'); // Invalid ``` TypeScript supports tuple and readonly tuple: ```typescript const x: [string, number] = ['a', 1]; const y: readonly [string, number] = ['a', 1]; ``` ### any The `any` data type represents literally "any" value, it is the default value when TypeScript cannot infer the type or is not specified. When using `any` TypeScript compiler skips the type checking so there is no type safety when `any` is being used. Generally do not use `any` to silence the compiler when an error occurs, instead focus on fixing the error as with using `any` it is possible to break contracts and we lose the benefits of TypeScript autocomplete. The `any` type could be useful during a gradual migration from JavaScript to TypeScript, as it can silence the compiler. For new projects use TypeScript configuration `noImplicitAny` which enables TypeScript to issue errors where `any` is used or inferred. The `any`type is usually a source of errors which can mask real problems with your types. Avoid using it as much as possible. ================================================ FILE: website/src/content/docs/book/readonly-properties.md ================================================ --- title: Readonly Properties sidebar: order: 13 label: 13. Readonly Properties --- Is it possible to prevent writing on a property by using the modifier `readonly`which makes sure that the property cannot be re-written but does not provide any guarantee of total immutability: ```typescript interface Y { readonly a: number; } type X = { readonly a: number; }; type J = Readonly<{ a: number; }>; type K = { readonly [index: number]: string; }; ``` ================================================ FILE: website/src/content/docs/book/strictnullchecks.md ================================================ --- title: strictNullChecks sidebar: order: 18 label: 18. strictNullChecks --- `strictNullChecks` is a TypeScript compiler option that enforces strict null checking. When this option is enabled, variables and parameters can only be assigned `null` or `undefined` if they have been explicitly declared to be of that type using the union type `null` | `undefined`. If a variable or parameter is not explicitly declared as nullable, TypeScript will generate an error to prevent potential runtime errors. ================================================ FILE: website/src/content/docs/book/symbols.md ================================================ --- title: Symbols sidebar: order: 58 label: 58. Symbols --- Symbols are a primitive data type that represents an immutable value which is guaranteed to be globally unique throughout the lifetime of the program. Symbols can be used as keys for object properties and provide a way to create non-enumerable properties. ```typescript const key1: symbol = Symbol('key1'); const key2: symbol = Symbol('key2'); const obj = { [key1]: 'value 1', [key2]: 'value 2', }; console.log(obj[key1]); // value 1 console.log(obj[key2]); // value 2 ``` In WeakMaps and WeakSets, symbols are now permissible as keys. ================================================ FILE: website/src/content/docs/book/table-of-contents.md ================================================ --- title: Table of Contents sidebar: order: 4 label: 4. Table of Contents --- - The Concise TypeScript Book - Translations - Downloads and website - Table of Contents - Introduction - About the Author - TypeScript Introduction - What is TypeScript? - Why TypeScript? - TypeScript and JavaScript - TypeScript Code Generation - Modern JavaScript Now (Downleveling) - Getting Started With TypeScript - Installation - Configuration - TypeScript Configuration File - target - lib - strict - module - moduleResolution - esModuleInterop - jsx - skipLibCheck - files - include - exclude - importHelpers - Migration to TypeScript Advice - Exploring the Type System - The TypeScript Language Service - Structural Typing - TypeScript Fundamental Comparison Rules - Types as Sets - Assign a type: Type Declarations and Type Assertions - Type Declaration - Type Assertion - Ambient Declarations - Property Checking and Excess Property Checking - Weak Types - Strict Object Literal Checking (Freshness) - Type Inference - More Advanced Inferences - Type Widening - Const - Const Modifier on Type Parameters - Const assertion - Explicit Type Annotation - Type Narrowing - Conditions - Throwing or returning - Discriminated Union - User-Defined Type Guards - Primitive Types - string - boolean - number - bigInt - Symbol - null and undefined - Array - any - Type Annotations - Optional Properties - Readonly Properties - Index Signatures - Extending Types - Literal Types - Literal Inference - strictNullChecks - Enums - Numeric enums - String enums - Constant enums - Reverse mapping - Ambient enums - Computed and constant members - Narrowing - typeof type guards - Truthiness narrowing - Equality narrowing - In Operator narrowing - instanceof narrowing - Assignments - Control Flow Analysis - Type Predicates - Discriminated Unions - The never Type - Exhaustiveness checking - Object Types - Tuple Type (Anonymous) - Named Tuple Type (Labeled) - Fixed Length Tuple - Union Type - Intersection Types - Type Indexing - Type from Value - Type from Func Return - Type from Module - Mapped Types - Mapped Type Modifiers - Conditional Types - Distributive Conditional Types - infer Type Inference in Conditional Types - Predefined Conditional Types - Template Union Types - Any type - Unknown type - Void type - Never type - Interface and Type - Common Syntax - Basic Types - Objects and Interfaces - Union and Intersection Types - Built-in Type Primitives - Common Built-in JS Objects - Overloads - Merging and Extension - Differences between Type and Interface - Class - Class Common Syntax - Constructor - Private and Protected Constructors - Access Modifiers - Get and Set - Auto-Accessors in Classes - this - Parameter Properties - Abstract Classes - With Generics - Decorators - Class Decorators - Property Decorator - Method Decorator - Getter and Setter Decorators - Decorator Metadata - Inheritance - Statics - Property initialization - Method overloading - Generics - Generic Type - Generic Classes - Generic Constraints - Generic contextual narrowing - Erased Structural Types - Namespacing - Symbols - Triple-Slash Directives - Type Manipulation - Creating Types from Types - Indexed Access Types - Utility Types - Awaited\ - Partial\ - Required\ - Readonly\ - Record\ - Pick\ - Omit\ - Exclude\ - Extract\ - NonNullable\ - Parameters\ - ConstructorParameters\ - ReturnType\ - InstanceType\ - ThisParameterType\ - OmitThisParameter\ - ThisType\ - Uppercase\ - Lowercase\ - Capitalize\ - Uncapitalize\ - NoInfer\ - Others - Errors and Exception Handling - Mixin classes - Asynchronous Language Features - Iterators and Generators - TsDocs JSDoc Reference - @types - JSX - ES6 Modules - ES7 Exponentiation Operator - The for-await-of Statement - New target meta-property - Dynamic Import Expressions - "tsc –watch" - Non-null Assertion Operator - Defaulted declarations - Optional Chaining - Nullish coalescing operator - Template Literal Types - Function overloading - Recursive Types - Recursive Conditional Types - ECMAScript Module Support in Node - Assertion Functions - Variadic Tuple Types - Boxed types - Covariance and Contravariance in TypeScript - Optional Variance Annotations for Type Parameters - Template String Pattern Index Signatures - The satisfies Operator - Type-Only Imports and Export - using declaration and Explicit Resource Management - await using declaration - Import Attributes ================================================ FILE: website/src/content/docs/book/template-union-types.md ================================================ --- title: Template Union Types sidebar: order: 43 label: 43. Template Union Types --- Template union types can be used to merge and manipulate text inside the type system for instance: ```typescript type Status = 'active' | 'inactive'; type Products = 'p1' | 'p2'; type ProductId = `id-${Products}-${Status}`; // "id-p1-active" | "id-p1-inactive" | "id-p2-active" | "id-p2-inactive" ``` ================================================ FILE: website/src/content/docs/book/the-concise-typescript-book.md ================================================ --- title: The Concise TypeScript Book sidebar: order: 1 label: 1. The Concise TypeScript Book --- The Concise TypeScript Book provides a comprehensive and succinct overview of TypeScript's capabilities. It offers clear explanations covering all aspects found in the latest version of the language, from its powerful type system to advanced features. Whether you're a beginner or an experienced developer, this book is an invaluable resource to enhance your understanding and proficiency in TypeScript. This book is completely Free and Open Source. I believe that high-quality technical education should be accessible to everyone, which is why I keep this book free and open. If the book helped you squash a bug, understand a tricky concept, or advance in your career, please consider supporting my work by paying what you want (suggested price: 15 USD) or sponsoring a coffee. Your support helps me keep the content up to date and expand it with new examples and deeper explanations. [![Buy Me a Coffee](https://img.shields.io/badge/buy_me_a_coffee-FFDD00?style=for-the-badge&logo=buy-me-a-coffee&logoColor=black)](https://www.buymeacoffee.com/simonepoggiali) [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/donate/?business=QW82ZS956XLFY&no_recurring=0¤cy_code=EUR) ================================================ FILE: website/src/content/docs/book/the-never-type.md ================================================ --- title: The never Type sidebar: order: 25 label: 25. The never Type --- When a variable is narrowed to a type that cannot contain any values, the TypeScript compiler will infer that the variable must be of the `never` type. This is because The never Type represents a value that can never be produced. ```typescript const printValue = (val: string | number) => { if (typeof val === 'string') { console.log(val.toUpperCase()); } else if (typeof val === 'number') { console.log(val.toFixed(2)); } else { // val has type never here because it can never be anything other than a string or a number const neverVal: never = val; console.log(`Unexpected value: ${neverVal}`); } }; ``` ================================================ FILE: website/src/content/docs/book/translations.md ================================================ --- title: Translations sidebar: order: 2 label: 2. Translations --- This book has been translated into several language versions, including: [Chinese](https://github.com/gibbok/typescript-book/blob/main/README-zh_CN.md) [Italian](https://github.com/gibbok/typescript-book/blob/main/README-it_IT.md) [Portuguese (Brazil)](https://github.com/gibbok/typescript-book/blob/main/README-pt_BR.md) [Swedish](https://github.com/gibbok/typescript-book/blob/main/README-sv_SE.md) ================================================ FILE: website/src/content/docs/book/triple-slash-directives.md ================================================ --- title: Triple-Slash Directives sidebar: order: 59 label: 59. Triple-Slash Directives --- Triple-slash directives are special comments that provide instructions to the compiler about how to process a file. These directives begin with three consecutive slashes (`///`) and are typically placed at the top of a TypeScript file and have no effects on the runtime behavior. Triple-slash directives are used to reference external dependencies, specify module loading behavior, enable/disable certain compiler features, and more. Few examples: Referencing a declaration file: ```typescript /// ``` Indicate the module format: ```typescript /// ``` Enable compiler options, in the following example strict mode: ```typescript /// ``` ================================================ FILE: website/src/content/docs/book/tuple-type-anonymous.md ================================================ --- title: Tuple Type (Anonymous) sidebar: order: 28 label: 28. Tuple Type (Anonymous) --- A Tuple Type is a type that represents an array with a fixed number of elements and their corresponding types. A tuple type enforces a specific number of elements and their respective types in a fixed order. Tuple types are useful when you want to represent a collection of values with specific types, where the position of each element in the array has a specific meaning. ```typescript type Point = [number, number]; ``` ================================================ FILE: website/src/content/docs/book/type-annotations.md ================================================ --- title: Type Annotations sidebar: order: 11 label: 11. Type Annotations --- On variables declared using `var`, `let` and `const`, it is possible to optionally add a type: ```typescript const x: number = 1; ``` TypeScript does a good job of inferring types, especially when simple one, so these declarations in most cases are not necessary. On functions is possible to add type annotations to parameters: ```typescript function sum(a: number, b: number) { return a + b; } ``` The following is an example using a anonymous functions (so called lambda function): ```typescript const sum = (a: number, b: number) => a + b; ``` These annotation can be avoided when a default value for a parameter is present: ```typescript const sum = (a = 10, b: number) => a + b; ``` Return type annotations can be added to functions: ```typescript const sum = (a = 10, b: number): number => a + b; ``` This is useful especially for more complex functions as writing expliciting the return type before an implementation can help better think about the function. Generally consider annotating type signatures but not the body local variables and add types always to object literals. ================================================ FILE: website/src/content/docs/book/type-from-func-return.md ================================================ --- title: Type from Func Return sidebar: order: 35 label: 35. Type from Func Return --- Type from Func Return refers to the ability to automatically infer the return type of a function based on its implementation. This allows TypeScript to determine the type of the value returned by the function without explicit type annotations. ```typescript const add = (x: number, y: number) => x + y; // TypeScript can infer that the return type of the function is a number ``` ================================================ FILE: website/src/content/docs/book/type-from-module.md ================================================ --- title: Type from Module sidebar: order: 36 label: 36. Type from Module --- Type from Module refers to the ability to use a module's exported values to automatically infer their types. When a module exports a value with a specific type, TypeScript can use that information to automatically infer the type of that value when it is imported into another module. ```typescript // calc.ts export const add = (x: number, y: number) => x + y; // index.ts import { add } from 'calc'; const r = add(1, 2); // r is number ``` ================================================ FILE: website/src/content/docs/book/type-from-value.md ================================================ --- title: Type from Value sidebar: order: 34 label: 34. Type from Value --- Type from Value in TypeScript refers to the automatic inference of a type from a value or expression through type inference. ```typescript const x = 'x'; // TypeScript infers 'x' as a string literal with 'const' (immutable), but widens it to 'string' with 'let' (reassignable). ``` ================================================ FILE: website/src/content/docs/book/type-indexing.md ================================================ --- title: Type Indexing sidebar: order: 33 label: 33. Type Indexing --- Type indexing refers to the ability to define types that can be indexed by a key that is not known in advance, using an index signature to specify the type for properties that are not explicitly declared. ```typescript type Dictionary = { [key: string]: T; }; const myDict: Dictionary = { a: 'a', b: 'b' }; console.log(myDict['a']); // Returns a ``` ================================================ FILE: website/src/content/docs/book/type-manipulation.md ================================================ --- title: Type Manipulation sidebar: order: 60 label: 60. Type Manipulation --- ### Creating Types from Types Is it possible to create new types composing, manipulating or transforming existing types. Intersection Types (`&`): Allow you to combine multiple types into a single type: ```typescript type A = { foo: number }; type B = { bar: string }; type C = A & B; // Intersection of A and B const obj: C = { foo: 42, bar: 'hello' }; ``` Union Types (`|`): Allow you to define a type that can be one of several types: ```typescript type Result = string | number; const value1: Result = 'hello'; const value2: Result = 42; ``` Mapped Types: Allow you to transform the properties of an existing type to create new type: ```typescript type Mutable = { readonly [P in keyof T]: T[P]; }; type Person = { name: string; age: number; }; type ImmutablePerson = Mutable; // Properties become read-only ``` Conditional types: Allow you to create types based on some conditions: ```typescript type ExtractParam = T extends (param: infer P) => any ? P : never; type MyFunction = (name: string) => number; type ParamType = ExtractParam; // string ``` ### Indexed Access Types In TypeScript is it possible to access and manipulate the types of properties within another type using an index, `Type[Key]`. ```typescript type Person = { name: string; age: number; }; type AgeType = Person['age']; // number ``` ```typescript type MyTuple = [string, number, boolean]; type MyType = MyTuple[2]; // boolean ``` ### Utility Types Several built-in utility types can be used to manipulate types, below a list of the most common used: #### Awaited\ Constructs a type that recursively unwraps Promise types. ```typescript type A = Awaited>; // string ``` #### Partial\ Constructs a type with all properties of T set to optional. ```typescript type Person = { name: string; age: number; }; type A = Partial; // { name?: string | undefined; age?: number | undefined; } ``` #### Required\ Constructs a type with all properties of T set to required. ```typescript type Person = { name?: string; age?: number; }; type A = Required; // { name: string; age: number; } ``` #### Readonly\ Constructs a type with all properties of T set to readonly. ```typescript type Person = { name: string; age: number; }; type A = Readonly; const a: A = { name: 'Simon', age: 17 }; a.name = 'John'; // Invalid ``` #### Record\ Constructs a type with a set of properties K of type T. ```typescript type Product = { name: string; price: number; }; const products: Record = { apple: { name: 'Apple', price: 0.5 }, banana: { name: 'Banana', price: 0.25 }, }; console.log(products.apple); // { name: 'Apple', price: 0.5 } ``` #### Pick\ Constructs a type by picking the specified properties K from T. ```typescript type Product = { name: string; price: number; }; type Price = Pick; // { price: number; } ``` #### Omit\ Constructs a type by omitting the specified properties K from T. ```typescript type Product = { name: string; price: number; }; type Name = Omit; // { name: string; } ``` #### Exclude\ Constructs a type by excluding all values of type U from T. ```typescript type Union = 'a' | 'b' | 'c'; type MyType = Exclude; // b ``` #### Extract\ Constructs a type by extracting all values of type U from T. ```typescript type Union = 'a' | 'b' | 'c'; type MyType = Extract; // a | c ``` #### NonNullable\ Constructs a type by excluding null and undefined from T. ```typescript type Union = 'a' | null | undefined | 'b'; type MyType = NonNullable; // 'a' | 'b' ``` #### Parameters\ Extracts the parameter types of a function type T. ```typescript type Func = (a: string, b: number) => void; type MyType = Parameters; // [a: string, b: number] ``` #### ConstructorParameters\ Extracts the parameter types of a constructor function type T. ```typescript class Person { constructor( public name: string, public age: number ) {} } type PersonConstructorParams = ConstructorParameters; // [name: string, age: number] const params: PersonConstructorParams = ['John', 30]; const person = new Person(...params); console.log(person); // Person { name: 'John', age: 30 } ``` #### ReturnType\ Extracts the return type of a function type T. ```typescript type Func = (name: string) => number; type MyType = ReturnType; // number ``` #### InstanceType\ Extracts the instance type of a class type T. ```typescript class Person { name: string; constructor(name: string) { this.name = name; } sayHello() { console.log(`Hello, my name is ${this.name}!`); } } type PersonInstance = InstanceType; const person: PersonInstance = new Person('John'); person.sayHello(); // Hello, my name is John! ``` #### ThisParameterType\ Extracts the type of 'this' parameter from a function type T. ```typescript interface Person { name: string; greet(this: Person): void; } type PersonThisType = ThisParameterType; // Person ``` #### OmitThisParameter\ Removes the 'this' parameter from a function type T. ```typescript function capitalize(this: String) { return this[0].toUpperCase + this.substring(1).toLowerCase(); } type CapitalizeType = OmitThisParameter; // () => string ``` #### ThisType\ Servers as a market for a contextual `this` type. ```typescript type Logger = { log: (error: string) => void; }; let helperFunctions: { [name: string]: Function } & ThisType = { hello: function () { this.log('some error'); // Valid as "log" is a part of "this". this.update(); // Invalid }, }; ``` #### Uppercase\ Make uppercase the name of the input type T. ```typescript type MyType = Uppercase<'abc'>; // "ABC" ``` #### Lowercase\ Make lowercase the name of the input type T. ```typescript type MyType = Lowercase<'ABC'>; // "abc" ``` #### Capitalize\ Capitalize the name of the input type T. ```typescript type MyType = Capitalize<'abc'>; // "Abc" ``` #### Uncapitalize\ Uncapitalize the name of the input type T. ```typescript type MyType = Uncapitalize<'Abc'>; // "abc" ``` #### NoInfer\ NoInfer is a utility type designed to block the automatic inference of types within the scope of a generic function. Example: ```typescript // Automatic inference of types within the scope of a generic function. function fn(x: T[], y: T) { return x.concat(y); } const r = fn(['a', 'b'], 'c'); // Type here is ("a" | "b" | "c")[] ``` With NoInfer: ```typescript // Example function that uses NoInfer to prevent type inference function fn2(x: T[], y: NoInfer) { return x.concat(y); } const r2 = fn2(['a', 'b'], 'c'); // Error: Type Argument of type '"c"' is not assignable to parameter of type '"a" | "b"'. ``` ================================================ FILE: website/src/content/docs/book/type-predicates.md ================================================ --- title: Type Predicates sidebar: order: 23 label: 23. Type Predicates --- Type Predicates in TypeScript are functions that return a boolean value and are used to narrow the type of a variable to a more specific type. ```typescript const isString = (value: unknown): value is string => typeof value === 'string'; const foo = (bar: unknown) => { if (isString(bar)) { console.log(bar.toUpperCase()); } else { console.log('not a string'); } }; ``` ================================================ FILE: website/src/content/docs/book/typescript-introduction.md ================================================ --- title: TypeScript Introduction sidebar: order: 7 label: 7. TypeScript Introduction --- ### What is TypeScript? TypeScript is a strongly typed programming language that builds on JavaScript. It was originally designed by Anders Hejlsberg in 2012 and is currently developed and maintained by Microsoft as an open source project. TypeScript compiles to JavaScript and can be executed in any JavaScript runtime (e.g., a browser or Node.js on a server). It supports multiple programming paradigms such as functional, generic, imperative, and object-oriented programming, and is a compiled (transpiled) language that is converted into JavaScript before execution. ### Why TypeScript? TypeScript is a strongly typed language that helps prevent common programming mistakes and avoid certain kinds of run-time errors before the program is executed. A strongly typed language allows the developer to specify various program constraints and behaviors in the data type definitions, facilitating the ability to verify the correctness of the software and prevent defects. This is especially valuable in large-scale applications. Some of the benefits of TypeScript: * Static typing, optionally strongly typed * Type Inference * Access to ES6 and ES7 features * Cross-Platform and Cross-browser Compatibility * Tooling support with IntelliSense ### TypeScript and JavaScript TypeScript is written in `.ts` or `.tsx` files, while JavaScript files are written in `.js` or `.jsx`. Files with the extension `.tsx` or `.jsx` can contain JavaScript Syntax Extension JSX, which is used in React for UI development. TypeScript is a typed superset of JavaScript (ECMAScript 2015) in terms of syntax. All JavaScript code is valid TypeScript code, but the reverse is not always true. For instance, consider a function in a JavaScript file with the `.js` extension, such as the following: ```typescript const sum = (a, b) => a + b; ``` The function can be converted and used in TypeScript by changing the file extension to `.ts`. However, if the same function is annotated with TypeScript types, it cannot be executed in any JavaScript runtime without compilation. The following TypeScript code will produce a syntax error if it is not compiled: ```typescript const sum = (a: number, b: number): number => a + b; ``` TypeScript was designed to detect possible exceptions that can occur at runtime during compilation time by having the developer define the intent with type annotations. In addition, TypeScript can also catch issues if no type annotation is provided. For instance, the following code snippet does not specify any TypeScript types: ```typescript const items = [{ x: 1 }, { x: 2 }]; const result = items.filter(item => item.y); ``` In this case, TypeScript detects an error and reports: ```text Property 'y' does not exist on type '{ x: number; }'. ``` TypeScript's type system is largely influenced by the runtime behavior of JavaScript. For example, the addition operator (+), which in JavaScript can either perform string concatenation or numeric addition, is modeled in the same way in TypeScript: ```typescript const result = '1' + 1; // Result is of type string ``` The team behind TypeScript has made a deliberate decision to flag unusual usage of JavaScript as errors. For instance, consider the following valid JavaScript code: ```typescript const result = 1 + true; // In JavaScript, the result is equal 2 ``` However, TypeScript throws an error: ```text Operator '+' cannot be applied to types 'number' and 'boolean'. ``` This error occurs because TypeScript strictly enforces type compatibility, and in this case, it identifies an invalid operation between a number and a boolean. ### TypeScript Code Generation The TypeScript compiler has two main responsibilities: checking for type errors and compiling to JavaScript. These two processes are independent of each other. Types do not affect the execution of the code in a JavaScript runtime, as they are completely erased during compilation. TypeScript can still output JavaScript even in the presence of type errors. Here is an example of TypeScript code with a type error: ```typescript const add = (a: number, b: number): number => a + b; const result = add('x', 'y'); // Argument of type 'string' is not assignable to parameter of type 'number'. ``` However, it can still produce executable JavaScript output: ```typescript 'use strict'; const add = (a, b) => a + b; const result = add('x', 'y'); // xy ``` It is not possible to check TypeScript types at runtime. For example: ```typescript interface Animal { name: string; } interface Dog extends Animal { bark: () => void; } interface Cat extends Animal { meow: () => void; } const makeNoise = (animal: Animal) => { if (animal instanceof Dog) { // 'Dog' only refers to a type, but is being used as a value here. // ... } }; ``` As the types are erased after compilation, there is no way to run this code in JavaScript. To recognize types at runtime, we need to use another mechanism. TypeScript provides several options, with a common one being "tagged union". For example: ```typescript interface Dog { kind: 'dog'; // Tagged union bark: () => void; } interface Cat { kind: 'cat'; // Tagged union meow: () => void; } type Animal = Dog | Cat; const makeNoise = (animal: Animal) => { if (animal.kind === 'dog') { animal.bark(); } else { animal.meow(); } }; const dog: Dog = { kind: 'dog', bark: () => console.log('bark'), }; makeNoise(dog); ``` The property "kind" is a value that can be used at runtime to distinguish between objects in JavaScript. It is also possible for a value at runtime to have a type different from the one declared in the type declaration. For instance, if the developer has misinterpreted an API type and annotated it incorrectly. TypeScript is a superset of JavaScript, so the "class" keyword can be used as a type and value at runtime. ```typescript class Animal { constructor(public name: string) {} } class Dog extends Animal { constructor( public name: string, public bark: () => void ) { super(name); } } class Cat extends Animal { constructor( public name: string, public meow: () => void ) { super(name); } } type Mammal = Dog | Cat; const makeNoise = (mammal: Mammal) => { if (mammal instanceof Dog) { mammal.bark(); } else { mammal.meow(); } }; const dog = new Dog('Fido', () => console.log('bark')); makeNoise(dog); ``` In JavaScript, a "class" has a "prototype" property, and the "instanceof" operator can be used to test if the prototype property of a constructor appears anywhere in the prototype chain of an object. TypeScript has no effect on runtime performance, as all types will be erased. However, TypeScript does introduce some build time overhead. ### Modern JavaScript Now (Downleveling) TypeScript can compile code to any released version of JavaScript since ECMAScript 3 (1999). This means that TypeScript can transpile code from the latest JavaScript features to older versions, a process known as Downleveling. This allows the usage of modern JavaScript while maintaining maximum compatibility with older runtime environments. It's important to note that during transpilation to an older version of JavaScript, TypeScript may generate code that could incur a performance overhead compared to native implementations. Here are some of the modern JavaScript features that can be used in TypeScript: * ECMAScript modules instead of AMD-style "define" callbacks or CommonJS "require" statements. * Classes instead of prototypes. * Variables declaration using "let" or "const" instead of "var". * "for-of" loop or ".forEach" instead of the traditional "for" loop. * Arrow functions instead of function expressions. * Destructuring assignment. * Shorthand property/method names and computed property names. * Default function parameters. By leveraging these modern JavaScript features, developers can write more expressive and concise code in TypeScript. ================================================ FILE: website/src/content/docs/book/union-type.md ================================================ --- title: Union Type sidebar: order: 31 label: 31. Union Type --- A Union Type is a type that represents a value that can be one of several types. Union Types are denoted using the `|` symbol between each possible type. ```typescript let x: string | number; x = 'hello'; // Valid x = 123; // Valid ``` ================================================ FILE: website/src/content/docs/book/unknown-type.md ================================================ --- title: Unknown type sidebar: order: 45 label: 45. Unknown type --- In TypeScript, the `unknown` type represents a value that is of an unknown type. Unlike `any` type, which allows for any type of value, `unknown` requires a type check or assertion before it can be used in a specific way so no operations are permitted on an `unknown` without first asserting or narrowing to a more specific type. The `unknown` type is only assignable to any type and the `unknown` type itself, it is a type-safe alternative to `any`. ```typescript let value: unknown; let value1: unknown = value; // Valid let value2: any = value; // Valid let value3: boolean = value; // Invalid let value4: number = value; // Invalid ``` ```typescript const add = (a: unknown, b: unknown): number | undefined => typeof a === 'number' && typeof b === 'number' ? a + b : undefined; console.log(add(1, 2)); // 3 console.log(add('x', 2)); // undefined ``` ================================================ FILE: website/src/content/docs/book/void-type.md ================================================ --- title: Void type sidebar: order: 46 label: 46. Void type --- The `void` type is used to indicate that a function does not return a value. ```typescript const sayHello = (): void => { console.log('Hello!'); }; ``` ================================================ FILE: website/src/content/docs/index.mdx ================================================ --- title: TypeScript Book description: The Concise TypeScript Book template: splash hero: tagline: The Concise TypeScript Book provides a comprehensive and succinct overview of TypeScript's capabilities. It offers clear explanations covering all aspects found in the latest version of the language, from its powerful type system to advanced features. Whether you're a beginner or an experienced developer, this book is an invaluable resource to enhance your understanding and proficiency in TypeScript.

This book is completely Free and Open Source. actions: - text: Read now! link: /typescript-book/book/the-concise-typescript-book/ icon: right-arrow variant: primary - text: GitHub link: https://github.com/gibbok/typescript-book icon: github variant: secondary - text: X.com link: https://twitter.com/gibbok_coding icon: x.com variant: secondary --- ================================================ FILE: website/src/content/docs/it-it/book/about-the-author.md ================================================ --- title: Informazioni sull'autore sidebar: order: 6 label: 6. Informazioni sull'autore --- Simone Poggiali è uno Staff Engineer esperto, con una passione per la scrittura di codice di livello professionale fin dagli anni '90. Nel corso della sua carriera internazionale, ha contribuito a numerosi progetti per un'ampia gamma di clienti, dalle startup alle grandi organizzazioni. Aziende di spicco come HelloFresh, Siemens, O2, Leroy Merlin e Snowplow hanno beneficiato della sua competenza e dedizione. È possibile contattare Simone Poggiali sulle seguenti piattaforme: * LinkedIn: [https://www.linkedin.com/in/simone-poggiali](https://www.linkedin.com/in/simone-poggiali) * GitHub: [https://github.com/gibbok](https://github.com/gibbok) * X.com: [https://x.com/gibbok_coding](https://x.com/gibbok_coding) * Email: gibbok.coding📧gmail.com Elenco completo dei collaboratori: [https://github.com/gibbok/typescript-book/graphs/contributors](https://github.com/gibbok/typescript-book/graphs/contributors) ================================================ FILE: website/src/content/docs/it-it/book/any-type.md ================================================ --- title: Tipo Any sidebar: order: 44 label: 44. Tipo Any --- Il tipo `any` è un tipo speciale (supertipo universale) che può essere utilizzato per rappresentare qualsiasi tipo di valore (primitive, oggetti, array, funzioni, errori, simboli). Viene spesso utilizzato in situazioni in cui il tipo di un valore non è noto in fase di compilazione, o quando si lavora con valori provenienti da API o librerie esterne che non dispongono di tipi TypeScript. Utilizzando il tipo `any`, si indica al compilatore TypeScript che i valori devono essere rappresentati senza alcuna limitazione. Per massimizzare la sicurezza dei tipi nel codice, tieni presente quanto segue: * Limitare l'utilizzo di `any` a casi specifici in cui il tipo è realmente sconosciuto. * Non restituire `any` da una funzione, poiché ciò indebolisce la sicurezza del tipo nel codice che lo utilizza. * Invece di `any`, utilizzare `@ts-ignore` se è necessario silenziare il compilatore. ```typescript let value: any; value = true; // Valido value = 7; // Valido ``` ================================================ FILE: website/src/content/docs/it-it/book/assignments.md ================================================ --- title: Assegnazioni sidebar: order: 21 label: 21. Assegnazioni --- Il restringimento TypeScript tramite assegnazioni è un modo per restringere il tipo di una variabile in base al valore assegnato. Quando a una variabile viene assegnato un valore, TypeScript ne deduce il tipo in base al valore assegnato e restringe il tipo della variabile in modo che corrisponda al tipo dedotto. ```typescript let value: string | number; value = 'hello'; if (typeof value === 'string') { console.log(value.toUpperCase()); } value = 42; if (typeof value === 'number') { console.log(value.toFixed(2)); } ``` ================================================ FILE: website/src/content/docs/it-it/book/built-in-type-primitives.md ================================================ --- title: Primitive di tipo predefinite sidebar: order: 49 label: 49. Primitive di tipo predefinite --- TypeScript dispone di diverse primitive di tipo predefinite che possono essere utilizzate per definire variabili, parametri di funzione e tipi restituiti: * `number`: rappresenta valori numerici, inclusi numeri interi e numeri in virgola mobile. * `string`: rappresenta dati testuali. * `boolean`: rappresenta valori logici, che possono essere true o false. * `null`: rappresenta l'assenza di un valore. * `undefined`: rappresenta un valore che non è stato assegnato o non è stato definito. * `symbol`: rappresenta un identificatore univoco. I simboli vengono in genere utilizzati come chiavi per le proprietà degli oggetti. * `bigint`: rappresenta numeri interi con precisione arbitraria. * `any`: rappresenta un tipo dinamico o sconosciuto. Le variabili di tipo any possono contenere valori di qualsiasi tipo e ignorano il controllo del tipo. \* `void`: rappresenta l'assenza di qualsiasi tipo. È comunemente usato come tipo di ritorno di funzioni che non restituiscono alcun valore. * `never`: rappresenta un tipo per valori che non si verificano mai. È tipicamente usato come tipo di ritorno di funzioni che generano un errore o entrano in un ciclo infinito. ================================================ FILE: website/src/content/docs/it-it/book/class.md ================================================ --- title: Classe sidebar: order: 54 label: 54. Classe --- ### Sintassi comune della classe La parola chiave `class` viene utilizzata in TypeScript per definire una classe. Di seguito è riportato un esempio: ```typescript class Person { private name: string; private age: number; constructor(name: string, age: number) { this.name = name; this.age = age; } public sayHi(): void { console.log(`Ciao, mi chiamo ${this.name} e ho ${this.age} anni.`); } } ``` La parola chiave `class` viene utilizzata per definire una classe denominata "Person". La classe ha due proprietà private: name di tipo `string` ed age di tipo `number`. Il costruttore viene definito utilizzando la parola chiave `constructor`. Accetta name ed age come parametri e li assegna alle proprietà corrispondenti. La classe ha un metodo `public` denominato sayHi che registra un messaggio di saluto. Per creare un'istanza di una classe in TypeScript, è possibile utilizzare la parola chiave `new` seguita dal nome della classe, seguito da parentesi `()`. Ad esempio: ```typescript const myObject = new Person('John Doe', 25); myObject.sayHi(); // Output: Ciao, mi chiamo John Doe e ho 25 anni. ``` ### Costruttore I costruttori sono metodi speciali all'interno di una classe che vengono utilizzati per inizializzare le proprietà dell'oggetto quando viene creata un'istanza della classe. ```typescript class Person { public name: string; public age: number; constructor(name: string, age: number) { this.name = name; this.age = age; } sayHello() { console.log(`Ciao, mi chiamo ${this.name} e ho ${this.age} anni.`); } } const john = new Person('Simon', 17); john.sayHello(); ``` È possibile sovraccaricare un costruttore utilizzando la seguente sintassi: ```typescript type Sex = 'm' | 'f'; class Person { name: string; age: number; sex: Sex; constructor(name: string, age: number, sex?: Sex); constructor(name: string, age: number, sex: Sex) { this.name = name; this.age = age; this.sex = sex ?? 'm'; } } const p1 = new Person('Simon', 17); const p2 = new Person('Alice', 22, 'f'); ``` In TypeScript, è possibile definire più overload del costruttore, ma è possibile avere una sola implementazione che deve essere compatibile con tutti gli overload; questo si può ottenere utilizzando un parametro opzionale. ```typescript class Person { name: string; age: number; constructor(); constructor(name: string); constructor(name: string, age: number); constructor(name?: string, age?: number) { this.name = name ?? 'Sconosciuto'; this.age = age ?? 0; } displayInfo() { console.log(`Nome: ${this.name}, Età: ${this.age}`); } } const person1 = new Person(); person1.displayInfo(); // Nome: unknown, Età: 0 const person2 = new Person('John'); person2.displayInfo(); // Nome: John, Età: 0 const person3 = new Person('Jane', 25); person3.displayInfo(); // Nome: Jane, Età: 25 ``` ### Costruttori privati ​​e protetti In TypeScript, i costruttori possono essere contrassegnati come privati ​​o protetti, il che ne limita l'accessibilità e l'utilizzo. Costruttori privati: possono essere chiamati solo all'interno della classe stessa. I costruttori privati ​​vengono spesso utilizzati in scenari in cui si desidera applicare un pattern singleton o limitare la creazione di istanze a un metodo factory all'interno della classe. Costruttori protetti: I costruttori protetti sono utili quando si desidera creare una classe base che non deve essere istanziata direttamente, ma può essere estesa tramite sottoclassi. ```typescript class BaseClass { protected constructor() {} } class DerivedClass extends BaseClass { private value: number; constructor(value: number) { super(); this.value = value; } } // Il tentativo di istanziare direttamente la classe base genererà un errore. // const baseObj = new BaseClass(); // Errore: il costruttore della classe 'BaseClass' è protetto. // Crea un'istanza della classe derivata const derivedObj = new DerivedClass(10); ``` ### Modificatori di accesso I modificatori di accesso `private`, `protected` e `public` vengono utilizzati per controllare la visibilità e l'accessibilità dei membri della classe, come proprietà e metodi, nelle classi TypeScript. Questi modificatori sono essenziali per applicare l'incapsulamento e stabilire limiti per l'accesso e la modifica dello stato interno di una classe. Il modificatore `private` limita l'accesso al membro della classe solo all'interno della classe contenitore. Il modificatore `protected` consente l'accesso al membro della classe all'interno della classe contenitore e delle sue classi derivate. Il modificatore `public` fornisce accesso illimitato al membro della classe, consentendone l'accesso da qualsiasi luogo. ### Get e Set Getter e setter sono metodi speciali che consentono di definire un comportamento personalizzato di accesso e modifica per le proprietà della classe. Consentono di incapsulare lo stato interno di un oggetto e di fornire logica aggiuntiva durante l'ottenimento o l'impostazione dei valori delle proprietà. In TypeScript, getter e setter sono definiti utilizzando rispettivamente le parole chiave `get` e `set`. Ecco un esempio: ```typescript class MyClass { private _myProperty: string; constructor(value: string) { this._myProperty = value; } get myProperty(): string { return this._myProperty; } set myProperty(value: string) { this._myProperty = value; } } ``` ### Accessori automatici nelle classi TypeScript versione 4.9 aggiunge il supporto per auto-accessor, una funzionalità ECMAScript di prossima uscita. Assomigliano alle proprietà di classe, ma sono dichiarate con la parola chiave "accessor". ```typescript class Animal { accessor name: string; constructor(name: string) { this.name = name; } } ``` Gli auto-accessor vengono "de-sugared" in accessor privati ​​`get` e `set`, che operano su una proprietà inaccessibile. ```typescript class Animal { #__name: string; get name() { return this.#__name; } set name(value: string) { this.#__name = name; } constructor(name: string) { this.name = name; } } ``` ### this In TypeScript, la parola chiave `this` si riferisce all'istanza corrente di una classe all'interno dei suoi metodi o costruttori. Permette di accedere e modificare le proprietà e i metodi della classe. dall'interno del proprio ambito. Fornisce un modo per accedere e manipolare lo stato interno di un oggetto all'interno dei propri metodi. ```typescript class Person { private name: string; constructor(name: string) { this.name = name; } public introduce(): void { console.log(`Ciao, mi chiamo ${this.name}.`); } } const person1 = new Person('Alice'); person1.introduce(); // Ciao, mi chiamo Alice. ``` ### Proprietà dei parametri Le proprietà dei parametri consentono di dichiarare e inizializzare le proprietà della classe direttamente all'interno dei parametri del costruttore, evitando codice boilerplate, ad esempio: ```typescript class Person { constructor( private name: string, public age: number ) { // Le parole chiave "private" e "public" nel costruttore // dichiarano e inizializzano automaticamente le proprietà della classe corrispondenti. } public introduce(): void { console.log(`Ciao, mi chiamo ${this.name} e ho ${this.age} anni.`); } } const person = new Person('Alice', 25); person.introduce(); ``` ### Classi astratte Le classi astratte sono utilizzate in TypeScript principalmente per l'ereditarietà, poiché forniscono un modo per definire proprietà e metodi comuni che possono essere ereditati dalle sottoclassi. Questo è utile quando si desidera definire un comportamento comune e imporre alle sottoclassi di implementare determinati metodi. Forniscono un modo per creare una gerarchia di classi in cui la classe base astratta fornisce un'interfaccia condivisa e funzionalità comuni per le sottoclassi. ```typescript abstract class Animal { protected name: string; constructor(name: string) { this.name = name; } abstract makeSound(): void; } class Cat extends Animal { makeSound(): void { console.log(`${this.name} miagola.`); } } const cat = new Cat('Whiskers'); cat.makeSound(); // Output: Whiskers miagola. ``` ### Con i generici Le classi con i generici consentono di definire classi riutilizzabili che possono funzionare con tipi diversi. ```typescript class Container { private item: T; constructor(item: T) { this.item = item; } getItem(): T { return this.item; } setItem(item: T): void { this.item = item; } } const container1 = new Container(42); console.log(container1.getItem()); // 42 const container2 = new Container('Hello'); container2.setItem('World'); console.log(container2.getItem()); // Mondo ``` ### Decoratori I decoratori forniscono un meccanismo per aggiungere metadati, modificare il comportamento, convalidare o estendere la funzionalità dell'elemento di destinazione. Sono funzioni che vengono eseguite in fase di esecuzione. È possibile applicare più decoratori a una dichiarazione. I decoratori sono funzionalità sperimentali e gli esempi seguenti sono compatibili solo con TypeScript versione 5 o successive che utilizzano ES6. Per le versioni di TypeScript precedenti alla 5, dovrebbero essere abilitati utilizzando la proprietà `experimentalDecorators` nel file `tsconfig.json` o utilizzando `--experimentalDecorators` nella riga di comando (ma l'esempio seguente non funzionerà). Alcuni dei casi d'uso comuni per i decoratori includono: * Monitoraggio delle modifiche delle proprietà. * Monitoraggio delle chiamate ai metodi. * Aggiunta di proprietà o metodi aggiuntivi. * Validazione in fase di esecuzione. * Serializzazione e deserializzazione automatica. * Registrazione. * Autorizzazione e autenticazione. * Protezione dagli errori. Nota: i decoratori per la versione 5 non consentono parametri di decorazione. Tipi di decoratori: #### Decoratori di classe I decoratori di classe sono utili per estendere una classe esistente, ad esempio aggiungendo proprietà o metodi o raccogliendo istanze di una classe. Nell'esempio seguente, aggiungiamo un metodo `toString` che converte la classe in una rappresentazione in formato stringa. ```typescript type Constructor = new (...args: any[]) => T; function toString( Value: Class, context: ClassDecoratorContext ) { return class extends Value { constructor(...args: any[]) { super(...args); console.log(JSON.stringify(this)); console.log(JSON.stringify(context)); } }; } @toString class Person { name: string; constructor(name: string) { this.name = name; } greet() { return 'Ciao,' + this.name; } } const person = new Person('Simon'); /* Log: {"name":"Simon"} {"kind":"class","name":"Person"} */ ``` #### Decoratore di proprietà I decoratori di proprietà sono utili per modificare il comportamento di una proprietà, ad esempio cambiando i valori di inizializzazione. Nel codice seguente, abbiamo uno script che imposta una proprietà in modo che sia sempre in maiuscolo: ```typescript function upperCase( target: undefined, context: ClassFieldDecoratorContext ) { return function (this: T, value: string) { return value.toUpperCase(); }; } class MyClass { @upperCase prop1 = 'hello!'; } console.log(new MyClass().prop1); // Log: HELLO! ``` #### Decoratore di metodo I decoratori di metodo consentono di modificare o migliorare il comportamento dei metodi. Di seguito è riportato un esempio di un semplice logger: ```typescript function log( target: (this: This, ...args: Args) => Return, context: ClassMethodDecoratorContext< This, (this: This, ...args: Args) => Return > ) { const methodName = String(context.name); function replacementMethod(this: This, ...args: Args): Return { console.log(`LOG: Accesso al metodo '${methodName}'.`); const result = target.call(this, ...args); console.log(`LOG: Uscita dal metodo '${methodName}'.`); return result; } return replacementMethod; } class MyClass { @log sayHello() { console.log('Ciao!'); } } new MyClass().sayHello(); ``` Registra: ```shell LOG: Accesso al metodo 'sayHello'. Ciao! LOG: Uscita dal metodo 'sayHello'. ``` #### Decoratori Getter e Setter I decoratori Getter e Setter consentono di modificare o migliorare il comportamento degli accessor di classe. Sono utili, ad esempio, per convalidare le assegnazioni di proprietà. Ecco un semplice esempio di decoratore getter: ```typescript function range(min: number, max: number) { return function ( target: (this: This) => Return, context: ClassGetterDecoratorContext ) { return function (this: This): Return { const value = target.call(this); if (value < min || value > max) { throw 'Invalid'; } Object.defineProperty(this, context.name, { value, enumerable: true, }); return value; }; }; } class MyClass { private _value = 0; constructor(value: number) { this._value = value; } @range(1, 100) get getValue(): number { return this._value; } } const obj = new MyClass(10); console.log(obj.getValue); // Valido: 10 const obj2 = new MyClass(999); console.log(obj2.getValue); // Throw: Invalid! ``` #### Metadati del decoratore I metadati del decoratore semplificano il processo per i decoratori di applicare e utilizzare i metadati in qualsiasi classe. Possono accedere a una nuova proprietà metadati sull'oggetto contesto, che può fungere da chiave sia per le primitive che per gli oggetti. Le informazioni sui metadati sono accessibili sulla classe tramite `Symbol.metadata`. I metadati possono essere utilizzati per vari scopi, come il debug, la serializzazione o l'iniezione di dipendenze con i decoratori. ```typescript //@ts-ignore Symbol.metadata ??= Symbol('Symbol.metadata'); // Simple polyfill type Context = | ClassFieldDecoratorContext | ClassAccessorDecoratorContext | ClassMethodDecoratorContext; // Il contesto contiene la proprietà metadati: DecoratorMetadata function setMetadata(_target: any, context: Context) { // Imposta l'oggetto metadati con un valore primitivo context.metadata[context.name] = true; } class MyClass { @setMetadata a = 123; @setMetadata accessor b = 'b'; @setMetadata fn() {} } const metadata = MyClass[Symbol.metadata]; // Ottieni informazioni sui metadati console.log(JSON.stringify(metadata)); // {"bar":true,"baz":true,"foo":true} ``` ### Ereditarietà L'ereditarietà si riferisce al meccanismo mediante il quale una classe può ereditare proprietà e metodi da un'altra classe, nota come classe base o superclasse. La classe derivata, chiamata anche classe figlia o sottoclasse, può estendere e specializzare le funzionalità della classe base aggiungendo nuove proprietà e metodi o sovrascrivendo quelli esistenti. ```typescript class Animal { name: string; constructor(name: string) { this.name = name; } speak(): void { console.log("L'animale emette un suono"); } } class Dog extends Animal { breed: string; constructor(name: string, breed: string) { super(name); this.breed = breed; } speak(): void { console.log('Woof! Woof!'); } } // Crea un'istanza della classe base const animal = new Animal('Animale generico'); animal.speak(); // L'animale emette un suono // Crea un'istanza della classe derivata const dog = new Dog('Max', 'Labrador'); dog.speak(); // Woof! Bau!" ``` TypeScript non supporta l'ereditarietà multipla nel senso tradizionale, ma consente invece l'ereditarietà da una singola classe base. TypeScript supporta più interfacce. Un'interfaccia può definire un contratto per la struttura di un oggetto e una classe può implementare più interfacce. Questo consente a una classe di ereditare comportamento e struttura da più fonti. ```typescript interface Flyable { fly(): void; } interface Swimmable { swim(): void; } class FlyingFish implements Flyable, Swimmable { fly() { console.log('Flying...'); } swim() { console.log('Swimming...'); } } const flyingFish = new FlyingFish(); flyingFish.fly(); flyingFish.swim(); ``` La parola chiave `class` in TypeScript, simile a JavaScript, è spesso definita "syntactic sugar". È stata introdotta in ECMAScript 2015 (ES6) offre una sintassi più familiare per la creazione e l'utilizzo di oggetti in modalità basata sulle classi. Tuttavia, è importante notare che TypeScript, essendo un superset di JavaScript, alla fine si compila in JavaScript, che rimane fondamentalmente basato sui prototipi. ### Statiche TypeScript ha membri statici. Per accedere ai membri statici di una classe, è possibile utilizzare il nome della classe seguito da un punto, senza dover creare un oggetto. ```typescript class OfficeWorker { static memberCount: number = 0; constructor(private name: string) { OfficeWorker.memberCount++; } } const w1 = new OfficeWorker('James'); const w2 = new OfficeWorker('Simon'); const total = OfficeWorker.memberCount; console.log(total); // 2 ``` ### Inizializzazione delle proprietà Esistono diversi modi per Inizializza le proprietà per una classe in TypeScript: Inline: Nell'esempio seguente, questi valori iniziali verranno utilizzati quando verrà creata un'istanza della classe. ```typescript class MyClass { property1: string = 'default value'; property2: number = 42; } ``` Nel costruttore: ```typescript class MyClass { property1: string; property2: number; constructor() { this.property1 = 'default value'; this.property2 = 42; } } ``` Utilizzo dei parametri del costruttore: ```typescript class MyClass { constructor( private property1: string = 'default value', public property2: number = 42 ) { // Non è necessario assegnare esplicitamente i valori alle proprietà. } log() { console.log(this.property2); } } const x = new MyClass(); x.log(); ``` ### Sovraccarico dei metodi Il sovraccarico dei metodi consente a una classe di avere più metodi con lo stesso nome ma tipi di parametri diversi o un numero diverso di parametri. Questo ci permette di chiamare un metodo in modi diversi in base agli argomenti passati. ```typescript class MyClass { add(a: number, b: number): number; // Firma di sovraccarico 1 add(a: string, b: string): string; // Firma di sovraccarico 2 add(a: number | string, b: number | string): number | string { if (typeof a === 'number' && typeof b === 'number') { return a + b; } if (typeof a === 'string' && typeof b === 'string') { return a.concat(b); } throw new Error('Argomenti non validi'); } } const r = new MyClass(); console.log(r.add(10, 5)); // Log 15 ``` ================================================ FILE: website/src/content/docs/it-it/book/common-built-in-js-objects.md ================================================ --- title: Oggetti JavaScript predefiniti comuni sidebar: order: 50 label: 50. Oggetti JavaScript predefiniti comuni --- TypeScript è un superset di JavaScript e include tutti gli oggetti JavaScript predefiniti comunemente usati. Un elenco completo di questi oggetti è disponibile sul sito web di documentazione di Mozilla Developer Network (MDN): [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects) Ecco un elenco di alcuni oggetti JavaScript predefiniti comunemente utilizzati: * Function * Object * Boolean * Error * Number * BigInt * Math * Date * String * RegExp * Array * Map * Set * Promise * Intl ================================================ FILE: website/src/content/docs/it-it/book/conditional-types.md ================================================ --- title: Tipi condizionali sidebar: order: 39 label: 39. Tipi condizionali --- I tipi condizionali sono un modo per creare un tipo che dipende da una condizione, in cui il tipo da creare viene determinato in base al risultato della condizione. Sono definiti utilizzando la parola chiave `extends` e un operatore ternario per scegliere condizionatamente tra due tipi. ```typescript type IsArray = T extends any[] ? true : false; const myArray = [1, 2, 3]; const myNumber = 42; type IsMyArrayAnArray = IsArray; // Type true type IsMyNumberAnArray = IsArray; // Type false ``` ================================================ FILE: website/src/content/docs/it-it/book/control-flow-analysis.md ================================================ --- title: Analisi del flusso di controllo sidebar: order: 22 label: 22. Analisi del flusso di controllo --- L'analisi del flusso di controllo in TypeScript è un modo per analizzare staticamente il flusso di codice per dedurre i tipi di variabili, consentendo al compilatore di restringere i tipi di tali variabili secondo necessità, in base ai risultati dell'analisi. Prima di TypeScript 4.4, l'analisi del flusso di codice veniva applicata solo al codice all'interno di un'istruzione if, ma da TypeScript 4.4 può essere applicata anche alle espressioni condizionali e agli accessi alle proprietà discriminanti referenziati indirettamente tramite variabili const. Ad esempio: ```typescript const f1 = (x: unknown) => { const isString = typeof x === 'string'; if (isString) { x.length; } }; const f2 = ( obj: { kind: 'foo'; foo: string } | { kind: 'bar'; bar: number } ) => { const isFoo = obj.kind === 'foo'; if (isFoo) { obj.foo; } else { obj.bar; } }; ``` Alcuni esempi in cui il restringimento non avviene: ```typescript const f1 = (x: unknown) => { let isString = typeof x === 'string'; if (isString) { x.length; // Errore, nessun restringimento perché isString non è costante } }; const f6 = ( obj: { kind: 'foo'; foo: string } | { kind: 'bar'; bar: number } ) => { const isFoo = obj.kind === 'foo'; obj = obj; if (isFoo) { obj.foo; // Errore, nessun restringimento perché obj è assegnato nel corpo della funzione } }; ``` Note: Nelle espressioni condizionali vengono analizzati fino a cinque livelli di indirezione. ================================================ FILE: website/src/content/docs/it-it/book/differences-between-type-and-interface.md ================================================ --- title: Differenze tra tipo e interfaccia sidebar: order: 53 label: 53. Differenze tra tipo e interfaccia --- Unione delle dichiarazioni (aumento): Le interfacce supportano l'unione delle dichiarazioni, il che significa che è possibile definire più interfacce con lo stesso nome e TypeScript le unirà in un'unica interfaccia con le proprietà e i metodi combinati. D'altra parte, i tipi non supportano l'unione delle dichiarazioni. Questo può essere utile quando si desidera aggiungere funzionalità extra o personalizzare i tipi esistenti senza modificare le definizioni originali o correggere tipi mancanti o errati. ```typescript interface A { x: string; } interface A { y: string; } const j: A = { x: 'xx', y: 'yy', }; ``` Estensione di altri tipi/interfacce: Sia i tipi che le interfacce possono estendere altri tipi/interfacce, ma la sintassi è diversa. Con le interfacce, si utilizza la parola chiave `extends` per ereditare proprietà e metodi da altre interfacce. Tuttavia, un'interfaccia non può estendere un tipo complesso come un tipo unione. ```typescript interface A { x: string; y: number; } interface B extends A { z: string; } const car: B = { x: 'x', y: 123, z: 'z', }; ``` Per i tipi, si utilizza l'operatore & per combinare più tipi in un unico tipo (intersezione). ```typescript interface A { x: string; y: number; } type B = A & { j: string; }; const c: B = { x: 'x', y: 123, j: 'j', }; ``` Tipi di unione e intersezione: I tipi sono più flessibili quando si tratta di definire tipi di unione e intersezione. Con la parola chiave `type`, è possibile creare facilmente tipi di unione utilizzando l'operatore `|` e tipi di intersezione utilizzando l'operatore `&`. Sebbene le interfacce possano anche rappresentare tipi di unione indirettamente, non dispongono di supporto integrato per i tipi di intersezione. ```typescript type Department = 'dep-x' | 'dep-y'; // Unione type Person = { name: string; age: number; }; type Employee = { id: number; department: Department; }; type EmployeeInfo = Person & Employee; // Intersezione ``` Esempio con interfacce: ```typescript interface A { x: 'x'; } interface B { y: 'y'; } type C = A | B; // Unione di interfacce ``` ================================================ FILE: website/src/content/docs/it-it/book/discriminated-unions.md ================================================ --- title: Unioni Discriminate sidebar: order: 24 label: 24. Unioni Discriminate --- Le unioni Discriminate in TypeScript sono un tipo di unione che utilizza una proprietà comune, nota come discriminante, per restringere l'insieme dei tipi possibili per l'unione. ```typescript type Square = { kind: 'square'; // Discriminante size: number; }; type Circle = { kind: 'circle'; // Discriminante radius: number; }; type Shape = Square | Circle; const area = (shape: Shape) => { switch (shape.kind) { case 'square': return Math.pow(shape.size, 2); case 'circle': return Math.PI * Math.pow(shape.radius, 2); } }; const square: Square = { kind: 'square', size: 5 }; const circle: Circle = { kind: 'circle', radius: 2 }; console.log(area(square)); // 25 console.log(area(circle)); // 12.566370614359172 ``` ================================================ FILE: website/src/content/docs/it-it/book/distributive-conditional-types.md ================================================ --- title: Tipi condizionali distributivi sidebar: order: 40 label: 40. Tipi condizionali distributivi --- I tipi condizionali distributivi sono una funzionalità che consente di distribuire un tipo su un'unione di tipi, applicando una trasformazione a ciascun membro dell'unione individualmente. Questo può essere particolarmente utile quando si lavora con tipi mappati o tipi di ordine superiore. ```typescript type Nullable = T extends any ? T | null : never; type NumberOrBool = number | boolean; type NullableNumberOrBool = Nullable; // number | boolean | null ``` ================================================ FILE: website/src/content/docs/it-it/book/downloads-and-website.md ================================================ --- title: Download e sito web sidebar: order: 3 label: 3. Download e sito web --- Puoi anche scaricare la versione Epub: [https://github.com/gibbok/typescript-book/tree/main/downloads](https://github.com/gibbok/typescript-book/tree/main/downloads) È disponibile una versione online su: [https://gibbok.github.io/typescript-book](https://gibbok.github.io/typescript-book) ================================================ FILE: website/src/content/docs/it-it/book/enums.md ================================================ --- title: Enumerazioni sidebar: order: 19 label: 19. Enumerazioni --- In TypeScript, un `enum` è un insieme di valori costanti denominati. ```typescript enum Colore { Rosso = '#ff0000', Verde = '#00ff00', Blu = '#0000ff', } ``` Gli enum possono essere definiti in diversi modi: ### Enumerazioni numeriche In TypeScript, un enum numerico è un enum in cui a ogni costante viene assegnato un valore numerico, a partire da 0 per impostazione predefinita. ```typescript enum Size { Small, // il valore inizia da 0 Medium, Large, } ``` È possibile specificare valori personalizzati assegnandoli esplicitamente: ```typescript enum Size { Small = 10, Medium, Large, } console.log(Size.Medium); // 11 ``` ### Enum String In TypeScript, un enum String è un enum in cui a ogni costante viene assegnato un valore stringa. ```typescript enum Language { English = 'EN', Spanish = 'ES', } ``` Nota: TypeScript consente l'utilizzo di enum eterogenei in cui stringhe e membri numerici possono coesistere. ### Enum Constant Un enum Constant in TypeScript è un tipo speciale di enum in cui tutti i valori sono noti in fase di compilazione e vengono inlineati ovunque venga utilizzato l'enum, con conseguente maggiore efficienza del codice. ```typescript const enum Language { English = 'EN', Spanish = 'ES', } console.log(Language.English); ``` Verrà compilato in: ```typescript console.log('EN' /* Language.English */); ``` Note: Gli enum costanti hanno valori hardcoded, che cancellano l'enum, il che può essere più efficiente nelle librerie autonome, ma generalmente non è auspicabile. Inoltre, gli enum costanti non possono avere membri calcolati. ### Mapping inverso In TypeScript, i mapping inversi negli enum si riferiscono alla possibilità di recuperare il nome del membro dell'enum dal suo valore. Per impostazione predefinita, i membri dell'enum hanno mapping in avanti dal nome al valore, ma i mapping inversi possono essere creati impostando esplicitamente i valori per ciascun membro. I mapping inversi sono utili quando è necessario cercare un membro dell'enum in base al suo valore o quando è necessario iterare su tutti i membri dell'enum. Si noti che solo i membri dell'enum numerico genereranno mapping inversi, mentre i membri dell'enum stringa non generano alcun mapping inverso. Il seguente enum: ```typescript enum Grade { A = 90, B = 80, C = 70, F = 'fail', } ``` Compila in: ```javascript "use strict"; var Grade; (function (Grade) { Grade[(Grade["A"] = 90)] = "A"; Grade[(Grade["B"] = 80)] = "B"; Grade[(Grade["C"] = 70)] = "C"; Grade["F"] = "fail"; })(Grade || (Grade = {})); ``` Pertanto, la mappatura dei valori alle chiavi funziona per i membri enum numerici, ma non per i membri enum stringa: ```typescript enum Grade { A = 90, B = 80, C = 70, F = 'fail', } const myGrade = Grade.A; console.log(Grade[myGrade]); // A console.log(Grade[90]); // A const failGrade = Grade.F; console.log(failGrade); // fail console.log(Grade[failGrade]); // Element ha implicitamente un tipo 'any' perché l'espressione indice non è di tipo 'number'. ``` ### Enum ambientali Un enum ambientale in TypeScript è un tipo di Enum definito in un file di dichiarazione (\*.d.ts) senza un'implementazione associata. Permette di definire un set di costanti denominate che possono essere utilizzate in modo sicuro tra file diversi senza dover importare i dettagli di implementazione in ogni file. ### Membri calcolati e costanti In TypeScript, un membro calcolato è un membro di un Enum il cui valore è calcolato in fase di esecuzione, mentre un membro costante è un membro il cui valore è impostato in fase di compilazione e non può essere modificato in fase di esecuzione. I membri calcolati sono consentiti negli Enum normali, mentre i membri costanti sono consentiti sia negli enum normali che in quelli costanti. ```typescript // Membri costanti enum Color { Red = 1, Green = 5, Blue = Red + Green, } console.log(Color.Blue); // 6 generazioni in fase di compilazione ``` ```typescript // Membri calcolati enum Color { Red = 1, Green = Math.pow(2, 2), Blue = Math.floor(Math.random() * 3) + 1, } console.log(Color.Blue); // numero casuale generato in fase di esecuzione ``` Gli enum sono indicati da unioni che comprendono i loro tipi di membri. I valori di ciascun membro possono essere determinati tramite espressioni costanti o non costanti, con i membri che possiedono valori costanti a cui vengono assegnati tipi letterali. Per illustrare, si consideri la dichiarazione del tipo E e dei suoi sottotipi E.A, E.B ed E.C. In questo caso, E rappresenta l'unione E.A | E.B | E.C. ```typescript const identity = (value: number) => value; enum E { A = 2 * 5, // Letterale numerico B = 'bar', // Letterale stringa C = identity(42), // Calcolato opaco } console.log(E.C); //42 ``` ================================================ FILE: website/src/content/docs/it-it/book/erased-structural-types.md ================================================ --- title: Tipi strutturali cancellati sidebar: order: 56 label: 56. Tipi strutturali cancellati --- In TypeScript, gli oggetti non devono necessariamente corrispondere a un tipo specifico ed esatto. Ad esempio, se creiamo un oggetto che soddisfa i requisiti di un'interfaccia, possiamo utilizzare quell'oggetto nei punti in cui l'interfaccia è richiesta, anche se non esiste una connessione esplicita tra i due. Esempio: ```typescript type NameProp1 = { prop1: string; }; function log(x: NameProp1) { console.log(x.prop1); } const obj = { prop2: 123, prop1: 'Origin', }; log(obj); // Valido ``` ================================================ FILE: website/src/content/docs/it-it/book/exhaustiveness-checking.md ================================================ --- title: Controllo di esaustività sidebar: order: 26 label: 26. Controllo di esaustività --- Il controllo di esaustività è una funzionalità di TypeScript che garantisce che tutti i possibili casi di unione discriminata vengano gestiti in un'istruzione `switch` o in un'istruzione `if`. ```typescript type Direction = 'up' | 'down'; const move = (direction: Direction) => { switch (direction) { case 'up': console.log("Spostamento verso l'alto"); break; case 'down': console.log('Spostamento verso il basso'); break; default: const exhaustiveCheck: never = direction; console.log(exhaustiveCheck); // Questa riga non verrà mai eseguita } }; ``` Il tipo `never` viene utilizzato per garantire che il caso predefinito sia esaustivo e che TypeScript generi un errore se un nuovo valore viene aggiunto al tipo Direction senza essere gestito nell'istruzione switch. ================================================ FILE: website/src/content/docs/it-it/book/exploring-the-type-system.md ================================================ --- title: Esplorazione del sistema di tipi sidebar: order: 9 label: 9. Esplorazione del sistema di tipi --- ### Il servizio di linguaggio TypeScript Il servizio di linguaggio TypeScript, noto anche come tsserver, offre diverse funzionalità come la segnalazione degli errori, la diagnostica, la compilazione al salvataggio, la ridenominazione, il passaggio alla definizione, gli elenchi di completamento, la guida alle firme e altro ancora. Viene utilizzato principalmente dagli ambienti di sviluppo integrati (IDE) per fornire supporto IntelliSense. Si integra perfettamente con Visual Studio Code ed è utilizzato da strumenti come Conquer of Completion (Coc). Gli sviluppatori possono sfruttare un'API dedicata e creare plugin di servizi linguistici personalizzati per migliorare l'esperienza di modifica di TypeScript. Questo può essere particolarmente utile per implementare funzionalità di linting speciali o abilitare il completamento automatico per un linguaggio di template personalizzato. Un esempio di plugin personalizzato reale è "TypeScript-styled-plugin", che fornisce la segnalazione degli errori di sintassi e il supporto IntelliSense per le proprietà CSS nei componenti con stile. Per ulteriori informazioni e guide rapide, è possibile consultare il Wiki ufficiale di TypeScript su GitHub: [https://github.com/microsoft/TypeScript/wiki/](https://github.com/microsoft/TypeScript/wiki/) ### Tipizzazione Strutturale TypeScript si basa su un sistema di tipi strutturale. Ciò significa che la compatibilità e l'equivalenza dei tipi sono determinate dalla struttura o definizione effettiva del tipo, piuttosto che dal suo nome o dal punto di dichiarazione, come nei sistemi di tipi nominativi come C# o C. Il sistema di tipi strutturale di TypeScript è stato progettato sulla base del funzionamento del sistema di tipizzazione dinamica di JavaScript durante l'esecuzione. L'esempio seguente è codice TypeScript valido. Come si può osservare, "X" e "Y" hanno lo stesso membro "a", anche se hanno nomi di dichiarazione diversi. I tipi sono determinati dalle loro strutture e, in questo caso, poiché le strutture sono le stesse, sono compatibili e validi. ```typescript type X = { a: string; }; type Y = { a: string; }; const x: X = { a: 'a' }; const y: Y = x; // Valido ``` ### Regole fondamentali di confronto di TypeScript Il processo di confronto di TypeScript è ricorsivo ed è eseguito su tipi annidati a qualsiasi livello. Un tipo "X" è compatibile con "Y" se "Y" ha almeno gli stessi membri di "X". ```typescript type X = { a: string; }; const y = { a: 'A', b: 'B' }; // Valido, poiché ha almeno gli stessi membri di X const r: X = y; ``` I parametri delle funzioni vengono confrontati in base al tipo, non al nome: ```typescript type X = (a: number) => void; type Y = (a: number) => void; let x: X = (j: number) => undefined; let y: Y = (k: number) => undefined; y = x; // Valido x = y; // Valido ``` I tipi restituiti dalla funzione devono essere gli stessi: ```typescript type X = (a: number) => undefined; type Y = (a: number) => number; let x: X = (a: number) => undefined; let y: Y = (a: number) => 1; y = x; // Non valido x = y; // Non valido ``` Il tipo di ritorno di una funzione sorgente deve essere un sottotipo del tipo di ritorno di una funzione target: ```typescript let x = () => ({ a: 'A' }); let y = () => ({ a: 'A', b: 'B' }); x = y; // Valido y = x; // Il membro non valido b è mancante ``` È consentito ignorare i parametri della funzione, come è prassi comune in JavaScript, ad esempio utilizzando "Array.prototype.map()": ```typescript [1, 2, 3].map((element, _index, _array) => element + 'x'); ``` Pertanto, le seguenti dichiarazioni di tipo sono completamente valide: ```typescript type X = (a: number) => undefined; type Y = (a: number, b: number) => undefined; let x: X = (a: number) => undefined; let y: Y = (a: number) => undefined; // Parametro b mancante y = x; // Valido ``` Tutti i parametri opzionali aggiuntivi del tipo sorgente sono validi: ```typescript type X = (a: number, b?: number, c?: number) => undefined; type Y = (a: number) => undefined; let x: X = a => undefined; let y: Y = a => undefined; y = x; // Valido x = y; //Valido ``` Tutti i parametri opzionali del tipo destinazione senza parametri corrispondenti nel tipo sorgente sono validi e non costituiscono un errore: ```typescript type X = (a: number) => undefined; type Y = (a: number, b?: number) => undefined; let x: X = a => undefined; let y: Y = a => undefined; y = x; // Valido x = y; // Valido ``` Il parametro rest viene trattato come una serie infinita di parametri opzionali: ```typescript type X = (a: number, ...rest: number[]) => undefined; let x: X = a => undefined; //valido ``` Le funzioni con overload sono valide se la firma di overload è compatibile con la firma della sua implementazione: ```typescript function x(a: string): void; function x(a: string, b: number): void; function x(a: string, b?: number): void { console.log(a, b); } x('a'); // Valido x('a', 1); // Valido function y(a: string): void; // Non valido, non compatibile con la firma dell'implementazione function y(a: string, b: number): void; function y(a: string, b: number): void { console.log(a, b); } y('a'); y('a', 1); ``` Il confronto dei parametri della funzione ha esito positivo se i parametri sorgente e destinazione sono assegnabili a supertipi o sottotipi (bivarianza). ```typescript // Supertipo class X { a: string; constructor(value: string) { this.a = value; } } // Sottotipo class Y extends X {} // Sottotipo class Z extends X {} type GetA = (x: X) => string; const getA: GetA = x => x.a; // La bivarianza accetta supertipi console.log(getA(new X('x'))); // Valido console.log(getA(new Y('Y'))); // Valido console.log(getA(new Z('z'))); // Valido ``` Gli enum sono confrontabili e validi con i numeri e viceversa, ma il confronto di valori Enum di tipi Enum diversi non è valido. ```typescript enum X { A, B, } enum Y { A, B, C, } const xa: number = X.A; // Valido const ya: Y = 0; // Valido X.A === Y.A; // Non valido ``` Le istanze di una classe sono soggette a un controllo di compatibilità per i loro membri privati ​​e protetti: ```typescript class X { public a: string; constructor(value: string) { this.a = value; } } class Y { private a: string; constructor(value: string) { this.a = value; } } let x: X = new Y('y'); // Non valido ``` Il controllo di confronto non tiene conto della diversa gerarchia di ereditarietà, ad esempio: ```typescript class X { public a: string; constructor(value: string) { this.a = value; } } class Y extends X { public a: string; constructor(value: string) { super(value); this.a = value; } } class Z { public a: string; constructor(value: string) { this.a = value; } } let x: X = new X('x'); let y: Y = new Y('y'); let z: Z = new Z('z'); x === y; // Valido x === z; // Valido anche se z proviene da una gerarchia di ereditarietà diversa ``` I generici vengono confrontati utilizzando le loro strutture in base al tipo risultante dopo l'applicazione del parametro generico; solo il risultato finale viene confrontato come tipo non generico. ```typescript interface X { a: T; } let x: X = { a: 1 }; let y: X = { a: 'a' }; x === y; // Non valido poiché l'argomento tipo è utilizzato nella struttura finale ``` ```typescript interface X {} const x: X = 1; const y: X = 'a'; x === y; // Valido poiché l'argomento tipo non è utilizzato nella struttura finale ``` Quando i generici non hanno il loro argomento tipo specificato, tutti gli argomenti non specificati vengono trattati come tipi con "any": ```typescript type X = (x: T) => T; type Y = (y: K) => K; let x: X = x => x; let y: Y = y => y; x = y; // Valido ``` Ricorda: ```typescript let a: number = 1; let b: number = 2; a = b; // Valido, tutto è assegnabile a se stesso let c: any; c = 1; // Valido, tutti i tipi sono assegnabili a qualsiasi let d: unknown; d = 1; // Valido, tutti i tipi sono assegnabili a sconosciuto let e: unknown; let e1: unknown = e; // Valido, sconosciuto è assegnabile solo a se stesso e a qualsiasi let e2: any = e; // Valido let e3: number = e; // Non valido let f: never; f = 1; // Non valido, nulla è assegnabile a never let g: void; let g1: any; g = 1; // Non valido, void non è assegnabile a o da nulla, tranne qualsiasi g = g1; // Valido ``` Si noti che quando "strictNullChecks" è abilitato, "null" e "undefined" vengono trattati in modo simile a "void"; in caso contrario, sono simili a "never". ### Tipi come insiemi In TypeScript, un tipo è un insieme di possibili valori. Questo insieme è anche definito dominio del tipo. Ogni valore di un tipo può essere visto come un elemento di un insieme. Un tipo stabilisce i vincoli che ogni elemento dell'insieme deve soddisfare per essere considerato membro di quell'insieme. Il compito principale di TypeScript è controllare e verificare se un insieme è un sottoinsieme di un altro. TypeScript supporta vari tipi di insiemi: | Termine di insieme | TypeScript | Note | | ------------------------------ | --------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | | Insieme vuoto | never | "never" non contiene nulla | | Insieme di un singolo elemento | undefined / null / tipo letterale | | | Insieme finito | boolean / union | | | Insieme infinito | string / number / object | | | Insieme universale | any / unknown | Ogni elemento è un membro di "any" e ogni insieme è un suo sottoinsieme / "unknown" è una controparte di tipo sicuro di "any" | Ecco alcuni esempi: | TypeScript | Termine di insieme | Esempio | | -------------- | --------------------------- | --------------------------------------------------------------------------------- | | never | ∅ (insieme vuoto) | const x: never = 'x'; // Errore: il tipo 'string' non è assegnabile al tipo 'never' | | | | | Tipo letterale | Insieme di elementi singoli | type X = 'X'; | | | | type Y = 7; | | | | | Valore assegnabile a T | Valore ∈ T (membro di) | type XY = 'X' \| 'Y'; | | | | const x: XY = 'X'; | | | | | T1 assegnabile a T2 | T1 ⊆ T2 (sottoinsieme di) | type XY = 'X' \| 'Y'; | | | | const x: XY = 'X'; | | | | const j: XY = 'J'; // Il tipo '"J"' non è assegnabile al tipo 'XY'. | | | | | | T1 extends T2 | T1 ⊆ T2 (sottoinsieme di) | type X = 'X' extends string ? true : false; | | | | | T1 \| T2 | T1 ∪ T2 (unione) | type XY = 'X' \| 'Y'; | | | | type JK = 1 \| 2; | | | | | T1 & T2 | T1 ∩ T2 (intersezione) | type X = \{ a: string \} | | | | type Y = \{ b: string \} | | | | type XY = X & Y | | | | const x: XY = \{ a: 'a', b: 'b' \} | | | | | unknown | Insieme universale | const x: unknown = 1 | Un'unione (T1 | T2) crea un insieme più ampio (entrambi): ```typescript type X = { a: string; }; type Y = { b: string; }; type XY = X | Y; const r: XY = { a: 'a', b: 'x' }; // Valido ``` Un'intersezione (T1 e T2) crea un insieme più ristretto (solo condiviso): ```typescript type X = { a: string; }; type Y = { a: string; b: string; }; type XY = X & Y; const r: XY = { a: 'a' }; // Non valido const j: XY = { a: 'a', b: 'b' }; // Valido ``` La parola chiave `extends` potrebbe essere considerata un "sottoinsieme di" in questo contesto. Imposta un vincolo per un tipo. L'extends utilizzato con un generico, considera il generico come un insieme infinito e lo vincola a un tipo più specifico. Si noti che ``extends` non ha nulla a che fare con la gerarchia in senso OOP (questo concetto non esiste in TypeScript). TypeScript funziona con insiemi e non ha una gerarchia rigida; infatti, come nell'esempio seguente, due tipi potrebbero sovrapporsi senza che uno dei due sia un sottotipo dell'altro (TypeScript considera la struttura e la forma degli oggetti). ```typescript interface X { a: string; } interface Y extends X { b: string; } interface Z extends Y { c: string; } const z: Z = { a: 'a', b: 'b', c: 'c' }; interface X1 { a: string; } interface Y1 { a: string; b: string; } interface Z1 { a: string; b: string; c: string; } const z1: Z1 = { a: 'a', b: 'b', c: 'c' }; const r: Z1 = z; // Valido ``` ### Assegnare un tipo: Dichiarazioni di tipo e asserzioni di tipo Un tipo può essere assegnato in diversi modi in TypeScript: #### Dichiarazione di tipo Nell'esempio seguente, utilizziamo x: X (": Type") per dichiarare un tipo per la variabile x. ```typescript type X = { a: string; }; // Dichiarazione di tipo const x: X = { a: 'a', }; ``` Se la variabile non è nel formato specificato, TypeScript segnalerà un errore. Ad esempio: ```typescript type X = { a: string; }; const x: X = { a: 'a', b: 'b', // Errore: il letterale dell'oggetto può specificare solo proprietà note }; ``` #### Asserzione di tipo È possibile aggiungere un'asserzione utilizzando la parola chiave `as`. Questo indica al compilatore che lo sviluppatore ha maggiori informazioni su un tipo e silenzia eventuali errori. Ad esempio: ```typescript type X = { a: string; }; const x = { a: 'a', b: 'b', } as X; ``` Nell'esempio precedente, si asserisce che l'oggetto x abbia il tipo X utilizzando la parola chiave as. Questo informa il compilatore TypeScript che l'oggetto è conforme al tipo specificato, anche se ha una proprietà aggiuntiva b non presente nella definizione del tipo. Le asserzioni di tipo sono utili in situazioni in cui è necessario specificare un tipo più specifico, soprattutto quando si lavora con il DOM. Ad esempio: ```typescript const myInput = document.getElementById('my_input') as HTMLInputElement; ``` Qui, l'asserzione di tipo come HTMLInputElement viene utilizzata per indicare a TypeScript che il risultato di getElementById deve essere trattato come un HTMLInputElement. Le asserzioni di tipo possono anche essere utilizzate per rimappare le chiavi, come mostrato nell'esempio seguente con letterali di template: ```typescript type J = { [Property in keyof Type as `prefix_${string & Property}`]: () => Type[Property]; }; type X = { a: string; b: number; }; type Y = J; ``` In questo esempio, il tipo `J` utilizza un tipo mappato con un letterale template per rimappare le chiavi di Tipo. Crea nuove proprietà con un "prefisso\_" aggiunto a ciascuna chiave e i valori corrispondenti sono funzioni che restituiscono i valori delle proprietà originali. È importante notare che quando si utilizza un'asserzione di tipo, TypeScript non eseguirà controlli di proprietà eccessivi. Pertanto, è generalmente preferibile utilizzare una Dichiarazione di Tipo quando la struttura dell'oggetto è nota in anticipo. #### Dichiarazioni Ambientali Le dichiarazioni Ambientali sono file che descrivono i tipi per il codice JavaScript e hanno un formato di nome file come `.d.ts.`. Di solito vengono importate e utilizzate per annotare librerie JavaScript esistenti o per aggiungere tipi a file JS esistenti nel progetto. Molti tipi di librerie comuni sono disponibili all'indirizzo: [https://github.com/DefinitelyTyped/DefinitelyTyped/](https://github.com/DefinitelyTyped/DefinitelyTyped/) e possono essere installate tramite: ```shell npm install --save-dev @types/library-name ``` Per le dichiarazioni di ambiente definite, è possibile importarle utilizzando il riferimento "tripla barra": ```typescript /// ``` È possibile utilizzare le dichiarazioni di ambiente anche all'interno di file JavaScript utilizzando `// @ts-check`. La parola chiave `declare` abilita le definizioni di tipo per il codice JavaScript esistente senza importarlo, fungendo da segnaposto per i tipi da un altro file o a livello globale. ### Controllo delle proprietà e controllo delle proprietà in eccesso TypeScript si basa su un sistema di tipi strutturale, ma il controllo delle proprietà in eccesso è una proprietà di TypeScript che gli consente di verificare se un oggetto possiede esattamente le proprietà specificate nel tipo. Il controllo delle proprietà in eccesso viene eseguito, ad esempio, quando si assegnano letterali di oggetto a variabili o quando li si passa come argomenti alla proprietà in eccesso di una funzione. ```typescript type X = { a: string; }; const y = { a: 'a', b: 'b' }; const x: X = y; // Valido perché tipizzazione strutturale const w: X = { a: 'a', b: 'b' }; // Non valido perché controllo delle proprietà in eccesso ``` ### Tipi deboli Un tipo è considerato debole quando contiene solo un insieme di proprietà completamente opzionali: ```typescript type X = { a?: string; b?: string; }; ``` TypeScript considera un errore assegnare qualsiasi cosa a un tipo debole quando non c'è sovrapposizione, ad esempio, il seguente codice genera un errore: ```typescript type Options = { a?: string; b?: string; }; const fn = (options: Options) => undefined; fn({ c: 'c' }); // Non valido ``` Sebbene non sia consigliato, se necessario, è possibile bypassare questo controllo utilizzando l'asserzione di tipo: ```typescript type Options = { a?: string; b?: string; }; const fn = (options: Options) => undefined; fn({ c: 'c' } as Options); // Valido ``` Oppure aggiungendo `unknown` alla firma dell'indice del tipo debole: ```typescript type Options = { [prop: string]: unknown; a?: string; b?: string; }; const fn = (options: Options) => undefined; fn({ c: 'c' }); // Valido ``` ### Controllo rigoroso dei letterali di oggetto (freschezza) Il controllo rigoroso dei letterali di oggetto, a volte chiamato "freschezza", è una funzionalità di TypeScript che aiuta a individuare proprietà in eccesso o con errori di ortografia che altrimenti passerebbero inosservate nei normali controlli di tipo strutturale. Quando si crea un letterale di oggetto, il compilatore TypeScript lo considera "fresco". Se il letterale di oggetto viene assegnato a una variabile o passato come parametro, TypeScript genererà un errore se il letterale di oggetto specifica proprietà che non esistono nel tipo di destinazione. Tuttavia, la "freschezza" scompare quando un letterale di oggetto viene ampliato o viene utilizzata un'asserzione di tipo. Ecco alcuni esempi per illustrare: ```typescript type X = { a: string }; type Y = { a: string; b: string }; let x: X; x = { a: 'a', b: 'b' }; // Controllo di freschezza: Assegnazione non valida var y: Y; y = { a: 'a', bx: 'bx' }; // Controllo di freschezza: Assegnazione non valida const fn = (x: X) => console.log(x.a); fn(x); fn(y); // Allargamento: Nessun errore, strutturalmente compatibile con il tipo fn({ a: 'a', bx: 'b' }); // Controllo di aggiornamento: argomento non valido let c: X = { a: 'a' }; let d: Y = { a: 'a', b: '' }; c = d; // Allargamento: nessun controllo di aggiornamento ``` ### Inferenza di tipo TypeScript può inferire i tipi quando non viene fornita alcuna annotazione durante: * Inizializzazione delle variabili. * Inizializzazione dei membri. * Impostazione dei valori predefiniti per i parametri. * Tipo di ritorno della funzione. Ad esempio: ```typescript let x = 'x'; // Il tipo inferito è una stringa ``` Il compilatore TypeScript analizza il valore o l'espressione e ne determina il tipo in base alle informazioni disponibili. ### Inferenze più avanzate Quando si utilizzano più espressioni nell'inferenza di tipo, TypeScript cerca i "tipi più comuni". Ad esempio: ```typescript let x = [1, 'x', 1, null]; // Il tipo dedotto è: (string | number | null)[] ``` Se il compilatore non riesce a trovare i tipi comuni migliori, restituisce un tipo unione. Ad esempio: ```typescript let x = [new RegExp('x'), new Date()]; // Il tipo inferito è: (RegExp | Date)[] ``` TypeScript utilizza la "tipizzazione contestuale" basata sulla posizione della variabile per inferire i tipi. Nell'esempio seguente, il compilatore sa che `e` è di tipo `MouseEvent` grazie al tipo di evento `click` definito nel file lib.d.ts, che contiene dichiarazioni ambientali per vari costrutti JavaScript comuni e il DOM: ```typescript window.addEventListener('click', function (e) {}); // Il tipo inferito di e è MouseEvent ``` ### Allargamento di tipo L'allargamento di tipo è il processo in cui TypeScript assegna un tipo a una variabile inizializzata quando non è stata fornita alcuna annotazione di tipo. Consente il passaggio da tipi stretti a più ampi, ma non viceversa. Nell'esempio seguente: ```typescript let x = 'x'; // TypeScript inferisce come stringa, un tipo ampio let y: 'y' | 'x' = 'y'; // Il tipo y è un'unione di tipi letterali y = x; // Il tipo non valido 'string' non è assegnabile al tipo '"x" | "y"'. ``` TypeScript assegna `string` a `x` in base al singolo valore fornito durante l'inizializzazione (`x`); questo è un esempio di ampliamento. TypeScript fornisce modi per controllare il processo di ampliamento, ad esempio utilizzando "const". ### Const L'utilizzo della parola chiave `const` durante la dichiarazione di una variabile produce un'inferenza di tipo più ristretta in TypeScript. Ad esempio: ```typescript const x = 'x'; // TypeScript deduce il tipo di x come 'x', un tipo più ristretto let y: 'y' | 'x' = 'y'; y = x; // Valido: il tipo di x viene dedotto come 'x' ``` Utilizzando `const` per dichiarare la variabile x, il suo tipo viene ristretto allo specifico valore letterale 'x'. Poiché il tipo di x viene ristretto, può essere assegnato alla variabile y senza errori. Il motivo per cui il tipo può essere dedotto è che le variabili `const` non possono essere riassegnate, quindi il loro tipo può essere ristretto a un tipo letterale specifico, in questo caso, il tipo letterale 'x'. #### Modificatore Const sui parametri di tipo Dalla versione 5.0 di TypeScript, è possibile specificare l'attributo `const` su un parametro di tipo generico. Questo consente di dedurre il tipo più preciso possibile. Vediamo un esempio senza usare `const`: ```typescript function identity(value: T) { // Nessuna costante qui return value; } const values = identity({ a: 'a', b: 'b' }); // Il tipo inferito è: { a: string; b: string; } ``` Come puoi vedere, le proprietà `a` e `b` vengono inferite con un tipo `string`. Ora, vediamo la differenza con la versione `const`: ```typescript function identity(value: T) { // Utilizzo del modificatore const sui parametri di tipo return value; } const values = identity({ a: 'a', b: 'b' }); // Il tipo inferito è: { a: "a"; b: "b"; } ``` Ora possiamo vedere che le proprietà `a` e `b` vengono dedotte come `const`, quindi `a` e `b` vengono trattate come stringhe letterali anziché come semplici tipi `string`. #### Asserzione Const Questa funzionalità consente di dichiarare una variabile con un tipo letterale più preciso in base al suo valore di inizializzazione, indicando al compilatore che il valore deve essere trattato come un letterale immutabile. Ecco alcuni esempi: Su una singola proprietà: ```typescript const v = { x: 3 as const, }; v.x = 3; ``` Su un intero oggetto: ```typescript const v = { x: 1, y: 2, } as const; ``` Questo può essere particolarmente utile quando si definisce il tipo per una tupla: ```typescript const x = [1, 2, 3]; // number[] const y = [1, 2, 3] as const; // Tupla di readonly [1, 2, 3] ``` ### Annotazione di tipo esplicita Possiamo essere specifici e passare un tipo, nell'esempio seguente la proprietà `x` è di tipo `number`: ```typescript const v = { x: 1, // Inferred type: number (widening) }; v.x = 3; // Valido ``` Possiamo rendere l'annotazione di tipo più specifica utilizzando un'unione di tipi letterali: ```typescript const v: { x: 1 | 2 | 3 } = { x: 1, // x è ora un'unione di tipi letterali: 1 | 2 | 3 }; v.x = 3; // Valido v.x = 100; // Non valido ``` ### Restringimento dei tipi Il restringimento dei tipi è il processo in TypeScript in cui un tipo generico viene ridotto a un tipo più specifico. Ciò si verifica quando TypeScript analizza il codice e determina che determinate condizioni o operazioni possono perfezionare le informazioni sul tipo. Il restringimento dei tipi può avvenire in diversi modi, tra cui: #### Condizioni Utilizzando istruzioni condizionali, come `if` o `switch`, TypeScript può restringere il tipo in base al risultato della condizione. Ad esempio: ```typescript let x: number | undefined = 10; if (x !== undefined) { x += 100; // Il tipo è number, che è stato ristretto dalla condizione } ``` #### Generazione o restituzione Generare un errore o restituire un'istruzione in anticipo da un branch può essere utilizzato per aiutare TypeScript a restringere un tipo. Ad esempio: ```typescript let x: number | undefined = 10; if (x === undefined) { throw 'error'; } x += 100; ``` Altri modi per restringere i tipi in TypeScript includono: * Operatore `instanceof`: utilizzato per verificare se un oggetto è un'istanza di una classe specifica. * Operatore `in`: utilizzato per verificare se una proprietà esiste in un oggetto. * Operatore `typeof`: utilizzato per verificare il tipo di un valore in fase di esecuzione. * Funzioni integrate come `Array.isArray()`: utilizzate per verificare se un valore è un array. #### Unione Discriminata L'utilizzo di una "Unione Discriminata" è un pattern in TypeScript in cui un "tag" esplicito viene aggiunto agli oggetti per distinguere i diversi tipi all'interno di un'unione. Questo pattern è anche definito "unione con tag". Nell'esempio seguente, il "tag" è rappresentato dalla proprietà "type": ```typescript type A = { type: 'type_a'; value: number }; type B = { type: 'type_b'; value: string }; const x = (input: A | B): string | number => { switch (input.type) { case 'type_a': return input.value + 100; // il tipo è A case 'type_b': return input.value + 'extra'; // il tipo è B } }; ``` #### Protezioni di tipo definite dall'utente Nei casi in cui TypeScript non sia in grado di determinare un tipo, è possibile scrivere una funzione di supporto nota come "protezione di tipo definita dall'utente". Nell'esempio seguente, utilizzeremo un predicato di tipo per restringere il tipo dopo aver applicato un determinato filtro: ```typescript const data = ['a', null, 'c', 'd', null, 'f']; const r1 = data.filter(x => x != null); // Il tipo è (string | null)[], TypeScript non è riuscito a dedurre correttamente il tipo const isValid = (item: string | null): item is string => item !== null; // Protezione personalizzata del tipo const r2 = data.filter(isValid); // Il tipo ora è corretto string[], utilizzando la protezione del tipo predicato siamo riusciti a restringere il tipo ``` ================================================ FILE: website/src/content/docs/it-it/book/extending-types.md ================================================ --- title: Estensione dei tipi sidebar: order: 15 label: 15. Estensione dei tipi --- È possibile estendere un'`interfaccia` (copiare membri da un altro tipo): ```typescript interface X { a: string; } interface Y extends X { b: string; } ``` È anche possibile estendere da più tipi: ```typescript interface A { a: string; } interface B { b: string; } interface Y extends A, B { y: string; } ``` La parola chiave `extends` funziona solo su interfacce e classi; per i tipi utilizzare un'intersezione: ```typescript type A = { a: number; }; type B = { b: number; }; type C = A & B; ``` È possibile estendere un tipo utilizzando un'inferenza, ma non viceversa: ```typescript type A = { a: string; }; interface B extends A { b: string; } ``` ================================================ FILE: website/src/content/docs/it-it/book/fixed-length-tuple.md ================================================ --- title: Tupla a lunghezza fissa sidebar: order: 30 label: 30. Tupla a lunghezza fissa --- Una tupla a lunghezza fissa è un tipo specifico di tupla che impone un numero fisso di elementi di tipi specifici e non consente alcuna modifica alla lunghezza della tupla una volta definita. Le tuple a lunghezza fissa sono utili quando è necessario rappresentare una raccolta di valori con un numero specifico di elementi e tipi specifici e si desidera garantire che la lunghezza e i tipi della tupla non possano essere modificati inavvertitamente. ```typescript const x = [10, 'hello'] as const; x.push(2); // Errore ``` ================================================ FILE: website/src/content/docs/it-it/book/generics.md ================================================ --- title: Generici sidebar: order: 55 label: 55. Generici --- I generici consentono di creare componenti e funzioni riutilizzabili che possono funzionare con più tipi. Con i generici, è possibile parametrizzare tipi, funzioni e interfacce, consentendo loro di operare su tipi diversi senza doverli specificare esplicitamente in anticipo. I generici consentono di rendere il codice più flessibile e riutilizzabile. ### Tipo generico Per definire un tipo generico, si utilizzano le parentesi angolari (`<>`) per specificare i parametri di tipo, ad esempio: ```typescript function identity(arg: T): T { return arg; } const a = identity('x'); const b = identity(123); const getLen = (data: ReadonlyArray) => data.length; const len = getLen([1, 2, 3]); ``` ### Classi generiche I generici possono essere applicati anche alle classi, in questo modo possono lavorare con più tipi utilizzando parametri di tipo. Questo è utile per creare definizioni di classe riutilizzabili che possono operare su diversi tipi di dati mantenendo la sicurezza dei tipi. ```typescript class Container { private item: T; constructor(item: T) { this.item = item; } getItem(): T { return this.item; } } const numberContainer = new Container(123); console.log(numberContainer.getItem()); // 123 const stringContainer = new Container('hello'); console.log(stringContainer.getItem()); // ciao ``` ### Vincoli generici I parametri generici possono essere vincolati utilizzando la parola chiave `extends` seguita da un tipo o un'interfaccia che il parametro di tipo deve soddisfare. Nell'esempio seguente, T deve contenere una `length` appropriata per essere valido: ```typescript const printLen = (value: T): void => { console.log(value.length); }; printLen('Ciao'); // 5 printLen([1, 2, 3]); // 3 printLen({ length: 10 }); // 10 printLen(123); // Non valido ``` Una caratteristica interessante di generic introdotta nella versione 3.4 RC è l'inferenza di tipo di funzione di ordine superiore, che ha introdotto argomenti di tipo generico propagati: ```typescript declare function pipe
( ab: (...args: A) => B, bc: (b: B) => C ): (...args: A) => C; declare function list(a: T): T[]; declare function box(x: V): { value: V }; const listBox = pipe(list, box); // (a: T) => { value: T[] } const boxList = pipe(box, list); // (x: V) => { value: V }[] ``` Questa funzionalità consente una programmazione più semplice, sicura e senza punti, comune nella programmazione funzionale. ### Restringimento contestuale generico Il restringimento contestuale per i generici è il meccanismo di TypeScript che consente al compilatore di restringere il tipo di un parametro generico in base al contesto in cui viene utilizzato. È utile quando si lavora con tipi generici in istruzioni condizionali: ```typescript function process(value: T): void { if (typeof value === 'string') { // Il valore viene ristretto al tipo 'string' console.log(value.length); } else if (typeof value === 'number') { // Il valore viene ristretto al tipo 'number' console.log(value.toFixed(2)); } } process('hello'); // 5 process(3.14159); // 3.14 ``` ================================================ FILE: website/src/content/docs/it-it/book/getting-started-with-typescript.md ================================================ --- title: Per iniziare con TypeScript sidebar: order: 8 label: 8. Per iniziare con TypeScript --- ### Installazione Visual Studio Code offre un eccellente supporto per il linguaggio TypeScript, ma non include il compilatore TypeScript. Per installare il compilatore TypeScript, è possibile utilizzare un gestore di pacchetti come npm o yarn: ```shell npm install typescript --save-dev ``` oppure ```shell yarn add typescript --dev ``` Assicurarsi di eseguire il commit del file di lock generato per garantire che ogni membro del team utilizzi la stessa versione di TypeScript. Per eseguire il compilatore TypeScript, è possibile utilizzare i seguenti comandi: ```shell npx tsc ``` oppure ```shell yarn tsc ``` Si consiglia di installare TypeScript a livello di progetto anziché globale, poiché garantisce un processo di build più prevedibile. Tuttavia, per occasioni particolari, è possibile utilizzare il seguente comando: ```shell npx tsc ``` oppure installarlo globalmente: ```shell npm install -g typescript ``` Se si utilizza Microsoft Visual Studio, è possibile ottenere TypeScript come pacchetto in NuGet per i progetti MSBuild. Nella console di Gestione Pacchetti di NuGet, eseguire il seguente comando: ```shell Install-Package Microsoft.TypeScript.MSBuild ``` Durante l'installazione di TypeScript, vengono installati due eseguibili: "tsc" come compilatore TypeScript e "tsserver" come server autonomo TypeScript. Il server autonomo contiene il compilatore e i servizi linguistici che possono essere utilizzati da editor e IDE per fornire il completamento intelligente del codice. Inoltre, sono disponibili diversi transpiler compatibili con TypeScript, come Babel (tramite un plugin) o swc. Questi transpiler possono essere utilizzati per convertire il codice TypeScript in altri linguaggi o versioni di destinazione. ### Configurazione TypeScript può essere configurato utilizzando le opzioni della CLI di tsc o un file di configurazione dedicato chiamato tsconfig.json, posizionato nella radice del progetto. Per generare un file tsconfig.json precompilato con le impostazioni consigliate, è possibile utilizzare il seguente comando: ```shell tsc --init ``` Quando si esegue il comando `tsc` localmente, TypeScript compilerà il codice utilizzando la configurazione specificata nel file tsconfig.json più vicino. Ecco alcuni esempi di comandi CLI che vengono eseguiti con le impostazioni predefinite: ```shell tsc main.ts // Compila un file specifico (main.ts) in JavaScript tsc src/*.ts // Compila tutti i file .ts nella cartella 'src' in JavaScript tsc app.ts util.ts --outfile index.js // Compila due file TypeScript (app.ts e util.ts) in un singolo file JavaScript (index.js) ``` ### File di configurazione TypeScript Un file tsconfig.json viene utilizzato per configurare il compilatore TypeScript (tsc). Solitamente, viene aggiunto alla radice del progetto, insieme al file `package.json`. Note: * tsconfig.json accetta commenti anche se è in formato json. * Si consiglia di utilizzare questo file di configurazione al posto delle opzioni della riga di comando. Al seguente link potete trovare la documentazione completa e il relativo schema: [https://www.typescriptlang.org/tsconfig](https://www.typescriptlang.org/tsconfig) [https://www.typescriptlang.org/tsconfig/](https://www.typescriptlang.org/tsconfig/) Di seguito è riportato un elenco delle configurazioni più comuni e utili: #### target La proprietà "target" viene utilizzata per specificare in quale versione di JavaScript ECMAScript TypeScript deve emettere/compilare. Per i browser moderni, ES6 è una buona opzione, mentre per i browser più vecchi si consiglia ES5. #### lib La proprietà "lib" viene utilizzata per specificare quali file di libreria includere in fase di compilazione. TypeScript include automaticamente le API per le funzionalità specificate nella proprietà "target", ma è possibile omettere o selezionare librerie specifiche per esigenze particolari. Ad esempio, se si lavora su un progetto server, è possibile escludere la libreria "DOM", utile solo in un ambiente browser. #### strict La proprietà "strict" offre garanzie più solide e migliora la sicurezza dei tipi. Si consiglia di includere sempre questa proprietà nel file tsconfig.json del progetto. Abilitando la proprietà "strict", TypeScript può: * Emettere codice utilizzando "use strict" per ogni file sorgente. * Considerare "null" e "undefined" nel processo di controllo dei tipi. * Disabilitare l'utilizzo del tipo "any" quando non sono presenti annotazioni di tipo. * Generare un errore sull'utilizzo dell'espressione "this", che altrimenti implicherebbe il tipo "any". #### module La proprietà "module" imposta il sistema di moduli supportato dal programma compilato. Durante l'esecuzione, un caricatore di moduli viene utilizzato per individuare ed eseguire le dipendenze in base al sistema di moduli specificato. I caricatori di moduli più comuni utilizzati in JavaScript sono Node.js CommonJS per le applicazioni lato server e RequireJS per i moduli AMD nelle applicazioni web basate su browser. TypeScript può generare codice per vari sistemi di moduli, tra cui UMD, System, ESNext, ES2015/ES6 ed ES2020. Nota: il sistema di moduli deve essere scelto in base all'ambiente di destinazione e al meccanismo di caricamento dei moduli disponibile in tale ambiente. #### moduleResolution La proprietà "moduleResolution" specifica la strategia di risoluzione dei moduli. Utilizzare "node" per il codice TypeScript moderno, la strategia "classic" viene utilizzata solo per le vecchie versioni di TypeScript (precedenti alla 1.6). #### esModuleInterop La proprietà "esModuleInterop" consente l'importazione predefinita dai moduli CommonJS che non sono stati esportati utilizzando la proprietà "default". Questa proprietà fornisce uno shim per garantire la compatibilità nel codice JavaScript emesso. Dopo aver abilitato questa opzione, possiamo usare `import MyLibrary from "my-library"` invece di `import * as MyLibrary from "my-library"`. #### jsx La proprietà "jsx" si applica solo ai file .tsx utilizzati in ReactJS e controlla il modo in cui i costrutti JSX vengono compilati in JavaScript. Un'opzione comune è "preserve", che compilerà in un file .jsx mantenendo invariato il codice JSX, in modo che possa essere passato a diversi strumenti come Babel per ulteriori trasformazioni. #### skipLibCheck La proprietà "skipLibCheck" impedisce a TypeScript di controllare il tipo di tutti i pacchetti di terze parti importati. Questa proprietà riduce il tempo di compilazione di un progetto. TypeScript controllerà comunque il codice rispetto alle definizioni di tipo fornite da questi pacchetti. #### files La proprietà "files" indica al compilatore un elenco di file che devono essere sempre inclusi nel programma. #### include La proprietà "include" indica al compilatore un elenco di file che si desidera includere. Questa proprietà consente schemi di tipo glob, come "\*_" per qualsiasi sottodirectory, "_" per qualsiasi nome di file e "?" per caratteri opzionali. #### exclude La proprietà "exclude" indica al compilatore un elenco di file che non devono essere inclusi nella compilazione. Questo può includere file come "node_modules" o file di test. Nota: tsconfig.json consente commenti. ### importHelpers TypeScript utilizza codice helper durante la generazione di codice per determinate funzionalità JavaScript avanzate o di livello inferiore. Per impostazione predefinita, questi helper vengono duplicati nei file che li utilizzano. L'opzione `importHelpers` importa invece questi helper dal modulo `tslib`, rendendo l'output JavaScript più efficiente. ### Consigli per la migrazione a TypeScript Per progetti di grandi dimensioni, si consiglia di adottare una transizione graduale in cui TypeScript e codice JavaScript coesisteranno inizialmente. Solo i progetti di piccole dimensioni possono essere migrati a TypeScript in un'unica soluzione. Il primo passo di questa transizione è introdurre TypeScript nel processo di build chain. Questo può essere fatto utilizzando l'opzione del compilatore "allowJs", che consente ai file .ts e .tsx di coesistere con i file JavaScript esistenti. Poiché TypeScript tornerà al tipo "any" per una variabile quando non riesce a dedurre il tipo dai file JavaScript, si consiglia di disabilitare "noImplicitAny" nelle opzioni del compilatore all'inizio della migrazione. Il secondo passaggio consiste nell'assicurarsi che i test JavaScript funzionino insieme ai file TypeScript, in modo da poterli eseguire durante la conversione di ciascun modulo. Se si utilizza Jest, si può valutare l'utilizzo di `ts-jest`, che consente di testare i progetti TypeScript con Jest. Il terzo passaggio consiste nell'includere le dichiarazioni di tipo per le librerie di terze parti nel progetto. Queste dichiarazioni sono disponibili in bundle o su DefinitelyTyped. È possibile cercarle utilizzando [https://www.typescriptlang.org/dt/search](https://www.typescriptlang.org/dt/search) e installarle tramite: ```shell npm install --save-dev @types/package-name ``` or ```shell yarn add --dev @types/package-name ``` Il quarto passaggio consiste nel migrare modulo per modulo con un approccio bottom-up, seguendo il grafo delle dipendenze partendo dalle foglie. L'idea è di iniziare a convertire i moduli che non dipendono da altri moduli. Per visualizzare i grafici delle dipendenze, è possibile utilizzare lo strumento "madge". I moduli candidati ideali per queste conversioni iniziali sono funzioni di utilità e codice relativo ad API o specifiche esterne. È possibile generare automaticamente definizioni di tipo TypeScript da contratti Swagger, schemi GraphQL o JSON da includere nel progetto. Quando non sono disponibili specifiche o schemi ufficiali, è possibile generare tipi da dati grezzi, come JSON restituiti da un server. Tuttavia, si consiglia di generare tipi da specifiche anziché da dati per evitare di perdere casi limite. Durante la migrazione, evitare il refactoring del codice e concentrarsi solo sull'aggiunta di tipi ai moduli. Il quinto passaggio consiste nell'abilitare "noImplicitAny", che garantirà che tutti i tipi siano noti e definiti, offrendo una migliore esperienza TypeScript per il progetto. Durante la migrazione, è possibile utilizzare la direttiva `@ts-check`, che abilita il controllo dei tipi TypeScript in un file JavaScript. Questa direttiva fornisce una versione semplificata del controllo dei tipi e può essere utilizzata inizialmente per identificare problemi nei file JavaScript. Quando `@ts-check` è incluso in un file, TypeScript tenterà di dedurre le definizioni utilizzando commenti in stile JSDoc. Tuttavia, si consiglia di utilizzare le annotazioni JSDoc solo in una fase molto precoce della migrazione. Si consiglia di mantenere il valore predefinito di `noEmitOnError` nel file tsconfig.json su false. Questo consentirà di generare codice sorgente JavaScript anche se vengono segnalati errori. ================================================ FILE: website/src/content/docs/it-it/book/index-signatures.md ================================================ --- title: Firme di indice sidebar: order: 14 label: 14. Firme di indice --- In TypeScript possiamo usare come firma di indice `string`, `number` e `symbol`: ```typescript type K = { [name: string | number]: string; }; const k: K = { x: 'x', 1: 'b' }; console.log(k['x']); console.log(k[1]); console.log(k['1']); // Stesso risultato di k[1] ``` Si noti che JavaScript converte automaticamente un indice con `number` in un indice con `string`, quindi `k[1]` o `k["1"]` restituiscono lo stesso valore. ================================================ FILE: website/src/content/docs/it-it/book/infer-type-inference-in-conditional-types.md ================================================ --- title: infer Inferenza di tipo nei tipi condizionali sidebar: order: 41 label: 41. infer Inferenza di tipo nei tipi condizionali --- La parola chiave `infer` viene utilizzata nei tipi condizionali per inferire (estrarre) il tipo di un parametro generico da un tipo che dipende da esso. Questo consente di scrivere definizioni di tipo più flessibili e riutilizzabili. ```typescript type ElementType = T extends (infer U)[] ? U : never; type Numbers = ElementType; // number type Strings = ElementType; // string ``` ================================================ FILE: website/src/content/docs/it-it/book/interface-and-type.md ================================================ --- title: Interfaccia e tipo sidebar: order: 48 label: 48. Interfaccia e tipo --- ### Sintassi comune In TypeScript, le interfacce definiscono la struttura degli oggetti, specificando i nomi e i tipi di proprietà o metodi che un oggetto deve avere. La sintassi comune per definire un'interfaccia in TypeScript è la seguente: ```typescript interface InterfaceName { property1: Type1; // ... method1(arg1: ArgType1, arg2: ArgType2): ReturnType; // ... } ``` Analogamente per la definizione del tipo: ```typescript type TypeName = { property1: Type1; // ... method1(arg1: ArgType1, arg2: ArgType2): ReturnType; // ... }; ``` `interface InterfaceName` o `type TypeName`: Definisce il nome dell'interfaccia. `property1`: `Type1`: Specifica le proprietà dell'interfaccia insieme ai tipi corrispondenti. È possibile definire più proprietà, ciascuna separata da un punto e virgola. `method1(arg1: ArgType1, arg2: ArgType2): ReturnType;`: Specifica i metodi dell'interfaccia. I metodi sono definiti con i loro nomi, seguiti da un elenco di parametri tra parentesi e dal tipo di ritorno. È possibile definire più metodi, ciascuno separato da un punto e virgola. Esempio di interfaccia: ```typescript interface Person { name: string; age: number; greet(): void; } ``` Esempio di tipo: ```typescript type TypeName = { property1: string; method1(arg1: string, arg2: string): string; }; ``` In TypeScript, i tipi vengono utilizzati per definire la forma dei dati e applicare il controllo dei tipi. Esistono diverse sintassi comuni per la definizione dei tipi in TypeScript, a seconda del caso d'uso specifico. Ecco alcuni esempi: ### Tipi di base ```typescript let myNumber: number = 123; // number type let myBoolean: boolean = true; // boolean type let myArray: string[] = ['a', 'b']; // array di stringhe let myTuple: [string, number] = ['a', 123]; // tupla ``` ### Oggetti e interfacce ```typescript const x: { name: string; age: number } = { name: 'Simon', age: 7 }; ``` ### Tipi di unione e intersezione ```typescript type MyType = string | number; // Union type let myUnion: MyType = 'hello'; // Can be a string myUnion = 123; // Or a number type TypeA = { name: string }; type TypeB = { age: number }; type CombinedType = TypeA & TypeB; // Intersection type let myCombined: CombinedType = { name: 'John', age: 25 }; // Object with name and age properties ``` ================================================ FILE: website/src/content/docs/it-it/book/intersection-types.md ================================================ --- title: Tipi Intersezione sidebar: order: 32 label: 32. Tipi Intersezione --- Un Tipo Intersezione è un tipo che rappresenta un valore che ha tutte le proprietà di due o più tipi. I Tipi Intersezione sono indicati con il simbolo `&` tra ogni tipo. ```typescript type X = { a: string; }; type Y = { b: string; }; type J = X & Y; // Intersezione const j: J = { a: 'a', b: 'b', }; ``` ================================================ FILE: website/src/content/docs/it-it/book/introduction.md ================================================ --- title: Introduzione sidebar: order: 5 label: 5. Introduzione --- Benvenuti a The Concise TypeScript Book! Questa guida vi fornirà le conoscenze essenziali e le competenze pratiche per uno sviluppo TypeScript efficace. Scoprite i concetti e le tecniche chiave per scrivere codice pulito e robusto. Che siate principianti o sviluppatori esperti, questo libro rappresenta sia una guida completa che un pratico riferimento per sfruttare la potenza di TypeScript nei vostri progetti. Questo libro tratta TypeScript 5.2. ================================================ FILE: website/src/content/docs/it-it/book/literal-inference.md ================================================ --- title: Inferenza letterale sidebar: order: 17 label: 17. Inferenza letterale --- L'inferenza letterale è una funzionalità di TypeScript che consente di dedurre il tipo di una variabile o di un parametro in base al suo valore. Nell'esempio seguente possiamo vedere che TypeScript considera `x` un tipo letterale in quanto il valore non può essere modificato in seguito, mentre `y` viene dedotto come stringa in quanto può essere modificato in seguito. ```typescript const x = 'x'; // Literal type of 'x', because this value cannot be changed let y = 'y'; // Type string, because we can change this value ``` Nell'esempio seguente possiamo vedere che `o.x` è stato dedotto come `string` (e non come un letterale di `a`), poiché TypeScript considera che il valore possa essere modificato in qualsiasi momento successivo. ```typescript type X = 'a' | 'b'; let o = { x: 'a', // Questa è una stringa più ampia }; const fn = (x: X) => `${x}-foo`; console.log(fn(o.x)); // L'argomento di tipo 'string' non è assegnabile al parametro di tipo 'X' ``` Come puoi vedere, il codice genera un errore quando si passa `o.x` a `fn`, poiché X è un tipo più ristretto. Possiamo risolvere questo problema utilizzando l'asserzione di tipo `const` o il tipo `X`: ```typescript let o = { x: 'a' as const, }; ``` oppure: ```typescript let o = { x: 'a' as X, }; ``` ================================================ FILE: website/src/content/docs/it-it/book/literal-types.md ================================================ --- title: Tipi letterali sidebar: order: 16 label: 16. Tipi letterali --- Un tipo letterale è un singolo insieme di elementi di un tipo collettivo; definisce un valore molto preciso che è una primitiva JavaScript. I tipi letterali in TypeScript sono numeri, stringhe e booleani. Esempio di letterali: ```typescript const a = 'a'; // Stringa tipo letterale const b = 1; // Numeric literal type const c = true; // Boolean literal type ``` I tipi letterali stringa, numerico e booleano vengono utilizzati nell'unione, nella protezione dei tipi e negli alias di tipo. Nell'esempio seguente, è possibile vedere un alias di tipo unione. `O` è costituito solo dai valori specificati, nessun'altra stringa è valida: ```typescript type O = 'a' | 'b' | 'c'; ``` ================================================ FILE: website/src/content/docs/it-it/book/mapped-type-modifiers.md ================================================ --- title: Modificatori di tipo mappati sidebar: order: 38 label: 38. Modificatori di tipo mappati --- I modificatori di tipo mappati in TypeScript consentono la trasformazione delle proprietà all'interno di un tipo esistente: * `readonly` o `+readonly`: questo rende una proprietà nel tipo mappato di sola lettura. * `-readonly`: questo consente a una proprietà nel tipo mappato di essere modificabile. * `?`: questo designa una proprietà nel tipo mappato come facoltativa. Esempi: ```typescript type ReadOnly = { readonly [P in keyof T]: T[P] }; // Tutte le proprietà contrassegnate come di sola lettura type Mutable = { -readonly [P in keyof T]: T[P] }; // Tutte le proprietà contrassegnate come modificabili type MyPartial = { [P in keyof T]?: T[P] }; // Tutte le proprietà contrassegnate come facoltative ``` ================================================ FILE: website/src/content/docs/it-it/book/mapped-types.md ================================================ --- title: Tipi mappati sidebar: order: 37 label: 37. Tipi mappati --- I tipi mappati in TypeScript consentono di creare nuovi tipi basati su un tipo esistente trasformando ciascuna proprietà tramite una funzione di mappatura. Mappando i tipi esistenti, è possibile creare nuovi tipi che rappresentano le stesse informazioni in un formato diverso. Per creare un tipo mappato, si accede alle proprietà di un tipo esistente utilizzando l'operatore `keyof` e quindi le si modifica per produrre un nuovo tipo. Nell'esempio seguente: ```typescript type MyMappedType = { [P in keyof T]: T[P][]; }; type MyType = { foo: string; bar: number; }; type MyNewType = MyMappedType; const x: MyNewType = { foo: ['hello', 'world'], bar: [1, 2, 3], }; ``` Definiamo MyMappedType per mappare le proprietà di T, creando un nuovo tipo con ogni proprietà come array del suo tipo originale. In questo modo, creiamo MyNewType per rappresentare le stesse informazioni di MyType, ma con ogni proprietà come array. ================================================ FILE: website/src/content/docs/it-it/book/merging-and-extension.md ================================================ --- title: Unione ed estensione sidebar: order: 52 label: 52. Unione ed estensione --- Merging ed estensione si riferiscono a due concetti diversi relativi all'utilizzo di tipi e interfacce. Merging consente di combinare più dichiarazioni con lo stesso nome in un'unica definizione, ad esempio quando si definisce un'interfaccia con lo stesso nome più volte: ```typescript interface X { a: string; } interface X { b: number; } const person: X = { a: 'a', b: 7, }; ``` L'estensione si riferisce alla possibilità di estendere o ereditare da tipi o interfacce esistenti per crearne di nuovi. È un meccanismo per aggiungere proprietà o metodi aggiuntivi a un tipo esistente senza modificarne la definizione originale. Esempio: ```typescript interface Animal { name: string; eat(): void; } interface Bird extends Animal { sing(): void; } const dog: Bird = { name: 'Bird 1', eat() { console.log('Eating'); }, sing() { console.log('Singing'); }, }; ``` ================================================ FILE: website/src/content/docs/it-it/book/named-tuple-type-labeled.md ================================================ --- title: Tipo di tupla denominato (etichettato) sidebar: order: 29 label: 29. Tipo di tupla denominato (etichettato) --- I tipi di tupla possono includere etichette o nomi opzionali per ciascun elemento. Queste etichette servono per migliorare la leggibilità e facilitare l'utilizzo degli strumenti e non influiscono sulle operazioni che è possibile eseguire con esse. ```typescript type T = string; type Tuple1 = [T, T]; type Tuple2 = [a: T, b: T]; type Tuple3 = [a: T, T]; // Tupla con nome più Tupla anonima ``` ================================================ FILE: website/src/content/docs/it-it/book/namespacing.md ================================================ --- title: Namespace sidebar: order: 57 label: 57. Namespace --- In TypeScript, gli spazi dei nomi vengono utilizzati per organizzare il codice in contenitori logici, prevenendo collisioni di nomi e fornendo un modo per raggruppare il codice correlato. L'utilizzo delle parole chiave `export` consente l'accesso allo spazio dei nomi nei moduli "esterni". ```typescript export namespace MyNamespace { export interface MyInterface1 { prop1: boolean; } export interface MyInterface2 { prop2: string; } } const a: MyNamespace.MyInterface1 = { prop1: true, }; ``` ================================================ FILE: website/src/content/docs/it-it/book/narrowing.md ================================================ --- title: Restringimento sidebar: order: 20 label: 20. Restringimento --- Il restringimento di TypeScript è il processo di perfezionamento del tipo di una variabile all'interno di un blocco condizionale. Questo è utile quando si lavora con tipi union, in cui una variabile può avere più di un tipo. TypeScript riconosce diversi modi per restringere il tipo: ### protezioni di tipo typeof Il type guard typeof è uno specifico type guard in TypeScript che controlla il tipo di una variabile in base al suo tipo JavaScript predefinito. ```typescript const fn = (x: number | string) => { if (typeof x === 'number') { return x + 1; // x è un numero } return -1; }; ``` ### Restringimento di veridicità Il restringimento di veridicità in TypeScript funziona verificando se una variabile è vera o falsa, per restringerne di conseguenza il tipo. ```typescript const toUpperCase = (name: string | null) => { if (name) { return name.toUpperCase(); } else { return null; } }; ``` ### Restringimento di uguaglianza Il restringimento di uguaglianza in TypeScript funziona verificando se una variabile è uguale o meno a un valore specifico, per restringerne di conseguenza il tipo. Viene utilizzato insieme alle istruzioni `switch` e agli operatori di uguaglianza come `===`, `!==`, `==` e `!=` per restringere i tipi. ```typescript const checkStatus = (status: 'success' | 'error') => { switch (status) { case 'success': return true; case 'error': return null; } }; ``` ### Restringimento dell'operatore `in` Il restringimento dell'operatore `in` in TypeScript è un modo per restringere il tipo di una variabile in base all'esistenza di una proprietà all'interno del tipo della variabile. ```typescript type Dog = { name: string; breed: string; }; type Cat = { name: string; likesCream: boolean; }; const getAnimalType = (pet: Dog | Cat) => { if ('breed' in pet) { return 'dog'; } else { return 'cat'; } }; ``` ### Restringimento instanceof L'operatore di restringimento `instanceof` in TypeScript è un modo per restringere il tipo di una variabile in base alla sua funzione costruttore, verificando se un oggetto è un'istanza di una determinata classe o interfaccia. ```typescript class Square { constructor(public width: number) {} } class Rectangle { constructor( public width: number, public height: number ) {} } function area(shape: Square | Rectangle) { if (shape instanceof Square) { return shape.width * shape.width; } else { return shape.width * shape.height; } } const square = new Square(5); const rectangle = new Rectangle(5, 10); console.log(area(square)); // 25 console.log(area(rectangle)); // 50 ``` ================================================ FILE: website/src/content/docs/it-it/book/never-type.md ================================================ --- title: Tipo Never sidebar: order: 47 label: 47. Tipo Never --- Il tipo `never` rappresenta valori che non si verificano mai. Viene utilizzato per indicare funzioni o espressioni che non restituiscono mai né generano errori. Ad esempio, un ciclo infinito: ```typescript const infiniteLoop = (): never => { while (true) { // fai qualcosa } }; ``` Generazione di un errore: ```typescript const throwError = (message: string): never => { throw new Error(message); }; ``` Il tipo `never` è utile per garantire la sicurezza dei tipi e rilevare potenziali errori nel codice. Aiuta TypeScript ad analizzare e dedurre tipi più precisi se utilizzato in combinazione con altri tipi e istruzioni di controllo del flusso, ad esempio: ```typescript type Direction = 'up' | 'down'; const move = (direction: Direction): void => { switch (direction) { case 'up': // sposta verso l'alto break; case 'down': // sposta verso il basso break; default: const exhaustiveCheck: never = direction; throw new Error(`Unhandled direction: ${exhaustiveCheck}`); } }; ``` ================================================ FILE: website/src/content/docs/it-it/book/object-types.md ================================================ --- title: Tipi di oggetto sidebar: order: 27 label: 27. Tipi di oggetto --- In TypeScript, i tipi di oggetto descrivono la forma di un oggetto. Specificano i nomi e i tipi delle proprietà dell'oggetto, nonché se tali proprietà sono obbligatorie o facoltative. In TypeScript, è possibile definire i tipi di oggetto in due modi principali: interface, che definisce la forma di un oggetto specificando i nomi, i tipi e l'opzionalità delle sue proprietà. ```typescript interface User { name: string; age: number; email?: string; } ``` Un alias di tipo, simile a un'interfaccia, definisce la forma di un oggetto. Tuttavia, può anche creare un nuovo tipo personalizzato basato su un tipo esistente o su una combinazione di tipi esistenti. Ciò include la definizione di tipi unione, tipi intersezione e altri tipi complessi. ```typescript type Point = { x: number; y: number; }; ``` È anche possibile definire un tipo in modo anonimo: ```typescript const sum = (x: { a: number; b: number }) => x.a + x.b; console.log(sum({ a: 5, b: 1 })); ``` ================================================ FILE: website/src/content/docs/it-it/book/optional-properties.md ================================================ --- title: Proprietà facoltative sidebar: order: 12 label: 12. Proprietà facoltative --- Un oggetto può specificare Proprietà facoltative aggiungendo un punto interrogativo `?` alla fine del nome della proprietà: ```typescript type X = { a: number; b?: number; // Facoltativo }; ``` È possibile specificare un valore predefinito quando una proprietà è facoltativa" ```typescript type X = { a: number; b?: number; }; const x = ({ a, b = 100 }: X) => a + b; ``` ================================================ FILE: website/src/content/docs/it-it/book/others.md ================================================ --- title: Altri sidebar: order: 61 label: 61. Altri --- ### Gestione degli errori e delle eccezioni TypeScript consente di rilevare e gestire gli errori utilizzando i meccanismi standard di gestione degli errori JavaScript: Blocchi Try-Catch-Finally: ```typescript try { // Codice che potrebbe generare un errore } catch (error) { // Gestisci l'errore } finally { // Codice che viene sempre eseguito, finally è facoltativo } ``` È anche possibile gestire diversi tipi di errore: ```typescript try { // Codice che potrebbe generare diversi tipi di errore } catch (error) { if (error instanceof TypeError) { // Gestisci TypeError } else if (error instanceof RangeError) { // Gestisci RangeError } else { // Gestisci altri errori } } ``` Tipi di errore personalizzati: È possibile specificare errori più specifici estendendo la classe `Error`: ```typescript class CustomError extends Error { constructor(message: string) { super(message); this.name = 'CustomError'; } } throw new CustomError('Questo è un errore personalizzato.'); ``` ### Classi Mixin Le classi Mixin consentono di combinare e comporre il comportamento di più classi in un'unica classe. Forniscono un modo per riutilizzare ed estendere le funzionalità senza la necessità di catene di ereditarietà profonde. ```typescript abstract class Identificabile { name: string = ''; logId() { console.log('id:', this.name); } } abstract class Selezionabile { selected: boolean = false; select() { this.selected = true; console.log('Select'); } deselect() { this.selected = false; console.log('Deselect'); } } class MyClass { constructor() {} } // Estendi MyClass per includere il comportamento di Identificabile e Selezionabile interface MyClass extends Identificabile, Selezionabile {} // Funzione per applicare i mixin a una classe function applyMixins(source: any, baseCtors: any[]) { baseCtors.forEach(baseCtor => { Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => { const descriptor = Object.getOwnPropertyDescriptor( baseCtor.prototype, name ); if (descriptor) { Object.defineProperty(source.prototype, name, descriptor); } }); }); } // Applica i mixin a MyClass applyMixins(MyClass, [Identificabile, Selezionabile]); let o = new MyClass(); o.name = 'abc'; o.logId(); o.select(); ``` ### Funzionalità del linguaggio asincrono Essendo TypeScript un superset di JavaScript, integra funzionalità del linguaggio asincrono come: Promise: Le promise sono un modo per gestire le operazioni asincrone e i loro risultati utilizzando metodi come `.then()` e `.catch()` per gestire le condizioni di successo e di errore. Per saperne di più: [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) Async/await: Le parole chiave Async/await sono un modo per fornire una sintassi più sincrona per lavorare con le promise. La parola chiave `async` viene utilizzata per definire una funzione asincrona, mentre la parola chiave `await` viene utilizzata all'interno di una funzione asincrona per mettere in pausa l'esecuzione finché una Promise non viene risolta o rifiutata. Per saperne di più: [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function) [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await) Le seguenti API sono ben supportate in TypeScript: API Fetch: [https://developer.mozilla.org/it/docs/Web/API/Fetch_API](https://developer.mozilla.org/it/docs/Web/API/Fetch_API) Web Worker: [https://developer.mozilla.org/it/docs/Web/API/Web_Workers_API](https://developer.mozilla.org/it/docs/Web/API/Web_Workers_API) Condiviso Worker: [https://developer.mozilla.org/en-US/docs/Web/API/SharedWorker](https://developer.mozilla.org/en-US/docs/Web/API/SharedWorker) WebSocket: [https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API) ### Iteratori e Generatori Sia gli Iteratori che i Generatori sono ben supportati in TypeScript. Gli Iteratori sono oggetti che implementano il protocollo Iterator, fornendo un modo per accedere agli elementi di una collezione o sequenza uno alla volta. Si tratta di una struttura che contiene un puntatore all'elemento successivo nell'iterazione. Hanno un metodo `next()` che restituisce il valore successivo nella sequenza insieme a un valore booleano che indica se la sequenza è `completata`. ```typescript class NumberIterator implements Iterable { private current: number; constructor( private start: number, private end: number ) { this.current = start; } public next(): IteratorResult { if (this.current <= this.end) { const value = this.current; this.current++; return { value, done: false }; } else { return { value: undefined, done: true }; } } [Symbol.iterator](): Iterator { return this; } } const iterator = new NumberIterator(1, 3); for (const num of iterator) { console.log(num); } ``` I generatori sono funzioni speciali definite utilizzando la sintassi `function*` che semplifica la creazione di iteratori. Utilizzano la parola chiave `yield` per definire la sequenza di valori e mettono automaticamente in pausa e riprendono l'esecuzione quando vengono richiesti valori. I generatori semplificano la creazione di iteratori e sono particolarmente utili per lavorare con sequenze di grandi dimensioni o infinite. Esempio: ```typescript function* numberGenerator(start: number, end: number): Generator { for (let i = start; i <= end; i++) { yield i; } } const generator = numberGenerator(1, 5); for (const num of generator) { console.log(num); } ``` TypeScript supporta anche iteratori e generatori asincroni. Per saperne di più: [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Generator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Generator) [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Iterator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Iterator) ### Riferimento JSDoc di TsDocs Quando si lavora con una base di codice JavaScript, è possibile aiutare TypeScript a dedurre il tipo corretto utilizzando commenti JSDoc con annotazioni aggiuntive per fornire informazioni sul tipo. Esempio: ```typescript /** * Calcola la potenza di un numero dato * @constructor * @param {number} base – Il valore base dell'espressione * @param {number} exponent – ​​Il valore esponente dell'espressione */ function power(base: number, exponent: number) { return Math.pow(base, exponent); } power(10, 2); // function power(base: number, exponent: number): number ``` La documentazione completa è disponibile a questo link: [https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html](https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html) Dalla versione 3.7 è possibile generare definizioni di tipo .d.ts dalla sintassi JavaScript JSDoc. Ulteriori informazioni sono disponibili qui: [https://www.typescriptlang.org/docs/handbook/declaration-files/dts-from-js.html](https://www.typescriptlang.org/docs/handbook/declaration-files/dts-from-js.html) ### @types I pacchetti nell'organizzazione @types sono convenzioni di denominazione speciali utilizzate per fornire definizioni di tipo per librerie o moduli JavaScript esistenti. Ad esempio, usando: ```shell npm install --save-dev @types/lodash ``` Installerà le definizioni di tipo di `lodash` nel tuo progetto corrente. Per contribuire alle definizioni di tipo del pacchetto @types, invia una pull request a [https://github.com/DefinitelyTyped/DefinitelyTyped](https://github.com/DefinitelyTyped/DefinitelyTyped). ### JSX JSX (JavaScript XML) è un'estensione della sintassi del linguaggio JavaScript che consente di scrivere codice simile a HTML all'interno dei file JavaScript o TypeScript. Viene comunemente utilizzato in React per definire la struttura HTML. TypeScript extends le funzionalità di JSX fornendo il controllo dei tipi e l'analisi statica. Per utilizzare JSX è necessario impostare l'opzione del compilatore `jsx` nel file `tsconfig.json`. Due opzioni di configurazione comuni: * "preserve": emette file .jsx con il JSX invariato. Questa opzione indica a TypeScript di mantenere la sintassi JSX così com'è e di non trasformarla durante il processo di compilazione. È possibile utilizzare questa opzione se si dispone di uno strumento separato, come Babel, che gestisce la trasformazione. * "react": abilita la trasformazione JSX integrata di TypeScript. Verrà utilizzato React.createElement. Tutte le opzioni sono disponibili qui: [https://www.typescriptlang.org/tsconfig#jsx](https://www.typescriptlang.org/tsconfig#jsx) ### Moduli ES6 TypeScript supporta ES6 (ECMAScript 2015) e molte versioni successive. Ciò significa che è possibile utilizzare la sintassi ES6, come funzioni freccia, letterali template, classi, moduli, destrutturazione e altro ancora. Per abilitare le funzionalità ES6 nel progetto, è possibile specificare la proprietà `target` nel file tsconfig.json. Un esempio di configurazione: ```json { "compilerOptions": { "target": "es6", "module": "es6", "moduleResolution": "node", "sourceMap": true, "outDir": "dist" }, "include": ["src"] } ``` ### Operatore di elevamento a potenza ES7 L'operatore di elevamento a potenza (`**`) calcola il valore ottenuto elevando il primo operando alla potenza del secondo operando. Funziona in modo simile a `Math.pow()`, ma con la possibilità aggiuntiva di accettare BigInt come operandi. TypeScript supporta pienamente questo operatore, utilizzandolo come `target` nel file tsconfig.json `es2016` o versione successiva. ```typescript console.log(2 ** (2 ** 2)); // 16 ``` ### L'istruzione for-await-of Questa è una funzionalità JavaScript completamente supportata in TypeScript che consente di iterare su oggetti iterabili asincroni dalla versione target es2018. ```typescript async function* asyncNumbers(): AsyncIterableIterator { yield Promise.resolve(1); yield Promise.resolve(2); yield Promise.resolve(3); } (async () => { for await (const num of asyncNumbers()) { console.log(num); } })(); ``` ### Nuova meta-proprietà target In TypeScript è possibile utilizzare la meta-proprietà `new.target`, che consente di determinare se una funzione o un costruttore è stato invocato utilizzando l'operatore new. Permette inoltre di rilevare se un oggetto è stato creato a seguito di una chiamata al costruttore. ```typescript class Parent { constructor() { console.log(new.target); // Registra la funzione costruttore utilizzata per creare un'istanza } } class Child extends Parent { constructor() { super(); } } const parentX = new Parent(); // [Function: Parent] const child = new Child(); // [Function: Child] ``` ### Espressioni di importazione dinamica È possibile caricare i moduli in modo condizionale o caricarli in modo differito su richiesta utilizzando la proposta ECMAScript per l'importazione dinamica, supportata in TypeScript. La sintassi per le espressioni di importazione dinamica in TypeScript è la seguente: ```typescript async function renderWidget() { const container = document.getElementById('widget'); if (container !== null) { const widget = await import('./widget'); // Importazione dinamica widget.render(container); } } renderWidget(); ``` ### "tsc –watch" Questo comando avvia un compilatore TypeScript con il parametro `--watch`, con la possibilità di ricompilare automaticamente i file TypeScript ogni volta che vengono modificati. ```shell tsc --watch ``` A partire dalla versione 4.9 di TypeScript, il monitoraggio dei file si basa principalmente sugli eventi del file system, ricorrendo automaticamente al polling se non è possibile stabilire un watcher basato sugli eventi. ### Operatore di asserzione non nullo L'operatore di asserzione non nullo (Postfix !), noto anche come asserzione di assegnazione definita, è una funzionalità di TypeScript che consente di affermare che una variabile o una proprietà non è nulla o indefinita, anche se l'analisi statica dei tipi di TypeScript suggerisce che potrebbe esserlo. Con questa funzionalità è possibile rimuovere qualsiasi controllo esplicito. ```typescript type Person = { name: string; }; const printName = (person?: Person) => { console.log(`Name is ${person!.name}`); }; ``` ### Dichiarazioni predefinite Le dichiarazioni predefinite vengono utilizzate quando a una variabile o a un parametro viene assegnato un valore predefinito. Ciò significa che se non viene fornito alcun valore per quella variabile o parametro, verrà utilizzato il valore predefinito. ```typescript function greet(name: string = 'Anonymous'): void { console.log(`Ciao, ${name}!`); } greet(); // Ciao, Anonymous! greet('John'); // Ciao, John! ``` ### Concatenamento opzionale L'operatore di concatenamento opzionale `?.` funziona come il normale operatore punto (`.`) per accedere a proprietà o metodi. Tuttavia, gestisce in modo elegante i valori nulli o indefiniti terminando l'espressione e restituendo `undefined`, invece di generare un errore. ```typescript type Person = { name: string; age?: number; address?: { street?: string; city?: string; }; }; const person: Person = { name: 'John', }; console.log(person.address?.city); // indefinito ``` ### Operatore di coalescenza nullo L'operatore di coalescenza nullo `??` restituisce il valore del lato destro se il lato sinistro è `null` o `undefined`; in caso contrario, restituisce il valore del lato sinistro. ```typescript const foo = null ?? 'foo'; console.log(foo); // foo const baz = 1 ?? 'baz'; const baz2 = 0 ?? 'baz'; console.log(baz); // 1 console.log(baz2); // 0 ``` ### Tipi letterali di template I tipi letterali modello consentono di manipolare i valori stringa a livello di tipo e di generare nuovi tipi stringa basati su quelli esistenti. Sono utili per creare tipi più espressivi e precisi da operazioni basate su stringhe. ```typescript type Department = 'engineering' | 'hr'; type Language = 'english' | 'spanish'; type Id = `${Department}-${Language}-id`; // "engineering-english-id" | "engineering-spanish-id" | "hr-english-id" | "hr-spanish-id" ``` ### Sovraccarico di funzioni Il sovraccarico di funzioni consente di definire più firme di funzione per lo stesso nome di funzione, ciascuna con tipi di parametro e tipo di ritorno diversi. Quando si chiama una funzione sovraccaricata, TypeScript utilizza gli argomenti forniti per determinare la firma di funzione corretta: ```typescript function makeGreeting(name: string): string; function makeGreeting(names: string[]): string[]; function makeGreeting(person: unknown): unknown { if (typeof person === 'string') { return `Ciao ${person}!`; } else if (Array.isArray(person)) { return person.map(name => `Ciao, ${name}!`); } throw new Error('Impossibile salutare'); } makeGreeting('Simon'); makeGreeting(['Simone', 'John']); ``` ### Tipi ricorsivi Un tipo ricorsivo è un tipo che può fare riferimento a se stesso. Questo è utile per definire strutture dati che hanno una struttura gerarchica o ricorsiva (annidamento potenzialmente infinito), come liste concatenate, alberi e grafi. ```typescript type ListNode = { data: T; next: ListNode | undefined; }; ``` ### Tipi condizionali ricorsivi È possibile definire relazioni di tipo complesse utilizzando la logica e la ricorsione in TypeScript. Analizziamole in termini semplici: Tipi condizionali: consente di definire tipi in base a condizioni booleane: ```typescript type CheckNumber = T extends number ? 'Number' : 'Not a number'; type A = CheckNumber<123>; // 'Number' type B = CheckNumber<'abc'>; // 'Not a number' ``` Ricorsione: indica una definizione di tipo che fa riferimento a se stessa all'interno della propria definizione: ```typescript type Json = string | number | boolean | null | Json[] | { [key: string]: Json }; const data: Json = { prop1: true, prop2: 'prop2', prop3: { prop4: [], }, }; ``` I tipi condizionali ricorsivi combinano sia la logica condizionale che la ricorsione. Ciò significa che una definizione di tipo può dipendere da se stessa tramite la logica condizionale, creando relazioni di tipo complesse e flessibili. ```typescript type Flatten = T extends Array ? Flatten : T; type NestedArray = [1, [2, [3, 4], 5], 6]; type FlattenedArray = Flatten; // 2 | 3 | 4 | 5 | 1 | 6 ``` ### Supporto per i moduli ECMAScript in Node Node.js ha aggiunto il supporto per i moduli ECMAScript a partire dalla versione 15.3.0, mentre TypeScript supporta i moduli ECMAScript per Node.js dalla versione 4.7. Questo supporto può essere abilitato utilizzando la proprietà `module` con il valore `nodenext` nel file tsconfig.json. Ecco un esempio: ```json { "compilerOptions": { "module": "nodenext", "outDir": "./lib", "declaration": true } } ``` Node.js supporta due estensioni di file per i moduli: `.mjs` per i moduli ES e `.cjs` per i moduli CommonJS. Le estensioni di file equivalenti in TypeScript sono `.mts` per i moduli ES e `.cts` per i moduli CommonJS. Quando il compilatore TypeScript trascrive questi file in JavaScript, creerà i file `.mjs` e `.cjs`. Se desideri utilizzare moduli ES nel tuo progetto, puoi impostare la proprietà `type` su "module" nel file package.json. Questo indica a Node.js di trattare il progetto come un progetto di modulo ES. Inoltre, TypeScript supporta anche le dichiarazioni di tipo nei file .d.ts. Questi file di dichiarazione forniscono informazioni sul tipo per librerie o moduli scritti in TypeScript, consentendo ad altri sviluppatori di utilizzarli con le funzionalità di controllo del tipo e di completamento automatico di TypeScript. ### Funzioni di asserzione In TypeScript, le funzioni di asserzione sono funzioni che indicano la verifica di una condizione specifica in base al loro valore di ritorno. Nella loro forma più semplice, una funzione di asserzione esamina un predicato fornito e genera un errore quando il predicato restituisce false. ```typescript function isNumber(value: unknown): asserts value is number { if (typeof value !== 'number') { throw new Error('Not a number'); } } ``` Oppure può essere dichiarato come espressione di funzione: ```typescript type AssertIsNumber = (value: unknown) => asserts value is number; const isNumber: AssertIsNumber = value => { if (typeof value !== 'number') { throw new Error('Not a number'); } }; ``` Le funzioni di asserzione condividono alcune somiglianze con le type guard. Le type guard sono state inizialmente introdotte per eseguire controlli in fase di esecuzione e garantire il tipo di un valore all'interno di un ambito specifico. Nello specifico, una type guard è una funzione che valuta un predicato di tipo e restituisce un valore booleano che indica se il predicato è vero o falso. Questo differisce leggermente dalle funzioni di asserzione, in cui l'intenzione è quella di generare un errore anziché restituire false quando il predicato non è soddisfatto. Esempio di type guard: ```typescript const isNumber = (value: unknown): value is number => typeof value === 'number'; ``` ### Tipi di tupla variadici I tipi di tupla variadici sono una funzionalità introdotta nella versione 4.0 di TypeScript. Iniziamo a conoscerli ripassando cos'è una tupla: Un tipo di tupla è un array di lunghezza definita, di cui è noto il tipo di ogni elemento: ```typescript type Student = [string, number]; const [name, age]: Student = ['Simone', 20]; ``` Il termine "variadico" significa indefinito (accetta un numero variabile di argomenti). Una tupla variadica è un tipo di tupla che ha tutte le proprietà di prima, ma la forma esatta non è ancora definita: ```typescript type Bar = [boolean, ...T, number]; type A = Bar<[boolean]>; // [booleano, booleano, numero] type B = Bar<['a', 'b']>; // [boolean, 'a', 'b', number] type C = Bar<[]>; // [boolean, number] ``` Nel codice precedente possiamo vedere che la forma della tupla è definita dal generico `T` passato. Le tuple variadiche possono accettare più generici, il che le rende molto flessibili: ```typescript type Bar = [...T, boolean, ...G]; type A = Bar<[number], [string]>; // [number, boolean, string] type B = Bar<['a', 'b'], [boolean]>; // ["a", "b", boolean, boolean] ``` Con le nuove tuple variadiche possiamo usare: * Gli spread nella sintassi dei tipi di tupla ora possono essere generici, quindi possiamo rappresentare operazioni di ordine superiore su tuple e array anche quando non conosciamo i tipi effettivi su cui stiamo operando. * Gli elementi rimanenti possono trovarsi ovunque in una tupla. Esempio: ```typescript type Items = readonly unknown[]; function concat( arr1: T, arr2: U ): [...T, ...U] { return [...arr1, ...arr2]; } concat([1, 2, 3], ['4', '5', '6']); // [1, 2, 3, "4", "5", "6"] ``` ### Tipi boxed I tipi boxed si riferiscono agli oggetti wrapper utilizzati per rappresentare i tipi primitivi come oggetti. Questi oggetti wrapper forniscono funzionalità e metodi aggiuntivi che non sono disponibili direttamente sui valori primitivi. Quando si accede a un metodo come `charAt` o `normalize` su una primitiva `string`, JavaScript lo racchiude in un oggetto `String`, chiama il metodo e quindi elimina l'oggetto. Dimostrazione: ```typescript const originalNormalize = String.prototype.normalize; String.prototype.normalize = function () { console.log(this, typeof this); return originalNormalize.call(this); }; console.log('\u0041'.normalize()); ``` TypeScript rappresenta questa differenziazione fornendo tipi separati per le primitive e i corrispondenti wrapper di oggetti: * string => String * number => Number * boolean => Boolean * symbol => Symbol * bigint => BigInt I tipi boxed di solito non sono necessari. Evitare di utilizzare tipi boxed e utilizzare invece type per le primitive, ad esempio `string` invece di `String`. ### Covarianza e Controvarianza in TypeScript Covarianza e Controvarianza vengono utilizzate per descrivere il funzionamento delle relazioni quando si ha a che fare con l'ereditarietà o l'assegnazione di tipi. Covarianza significa che una relazione di tipo preserva la direzione dell'ereditarietà o dell'assegnazione, quindi se un tipo A è un sottotipo del tipo B, anche un array di tipo A è considerato un sottotipo di un array di tipo B. La cosa importante da notare qui è che la relazione di sottotipo viene mantenuta, il che significa che Covarianza accetta il sottotipo ma non il supertipo. La controvarianza significa che una relazione di tipo inverte la direzione dell'ereditarietà o dell'assegnazione, quindi se un tipo A è un sottotipo del tipo B, allora un array di tipo B è considerato un sottotipo di un array di tipo A. La relazione di sottotipo è invertita, il che significa che la controvarianza accetta il supertipo ma non il sottotipo. Note: La bivarianza significa accettare sia il supertipo che il sottotipo. Esempio: supponiamo di avere uno spazio per tutti gli animali e uno spazio separato solo per i cani. In covarianza, puoi inserire tutti i cani nello spazio degli animali perché i cani sono un tipo di animale. Ma non puoi inserire tutti gli animali nello spazio dei cani perché potrebbero esserci altri animali mescolati. In controvarianza, non puoi inserire tutti gli animali nello spazio dei cani perché lo spazio degli animali potrebbe contenere anche altri animali. Tuttavia, puoi inserire tutti i cani nello spazio degli animali perché tutti i cani sono anche animali. ```typescript // Esempio di covarianza class Animal { name: string; constructor(name: string) { this.name = name; } } class Dog extends Animal { breed: string; constructor(name: string, breed: string) { super(name); this.breed = breed; } } let animals: Animal[] = []; let dogs: Dog[] = []; // La covarianza consente di assegnare l'array del sottotipo (Dog) all'array del supertipo (Animal) animals = dogs; dogs = animals; // Non valido: il tipo 'Animal[]' non è assegnabile al tipo 'Dog[]' // Esempio di controvarianza type Feed = (animal: T) => void; let feedAnimal: Feed = (animal: Animal) => { console.log(`Nome animale: ${animal.name}`); }; let feedDog: Feed = (dog: Dog) => { console.log(`Nome del cane: ${dog.name}, Razza: ${dog.breed}`); }; // La controvarianza consente di assegnare la callback del supertipo (Animal) alla callback del sottotipo (Dog) feedDog = feedAnimal; feedAnimal = feedDog; // Non valido: il tipo 'Feed' non è assegnabile al tipo 'Feed'. ``` In TypeScript, le relazioni di tipo per gli array sono covarianti, mentre le relazioni di tipo per i parametri di funzione sono controvarianti. Ciò significa che TypeScript presenta sia covarianza che controvarianza, a seconda del contesto. #### Annotazioni di varianza opzionali per i parametri di tipo A partire da TypeScript 4.7.0, possiamo usare le parole chiave `out` e `in` per specificare l'annotazione di varianza. Per la covarianza, usare la parola chiave `out`: ```typescript type AnimalCallback = () => T; // T è covariante in questo caso ``` E per la controvarianza, usare la parola chiave `in`: ```typescript type AnimalCallback = (value: T) => void; // T è controvariante in questo caso ``` ### Firme di indice con pattern di stringhe modello Le firme di indice con pattern di stringhe modello ci consentono di definire firme di indice flessibili utilizzando pattern di stringhe modello. Questa funzionalità ci consente di creare oggetti che possono essere indicizzati con pattern specifici di chiavi stringa, offrendo maggiore controllo e specificità durante l'accesso e la manipolazione delle proprietà. TypeScript dalla versione 4.4 consente firme di indice per simboli e pattern di stringhe modello. ```typescript const uniqueSymbol = Symbol('description'); type MyKeys = `key-${string}`; type MyObject = { [uniqueSymbol]: string; [key: MyKeys]: number; }; const obj: MyObject = { [uniqueSymbol]: 'Chiave simbolo univoca', 'key-a': 123, 'key-b': 456, }; console.log(obj[uniqueSymbol]); // Chiave simbolo univoca console.log(obj['key-a']); // 123 console.log(obj['key-b']); // 456 ``` ### Operatore `satisfies` L'operatore `satisfies` consente di verificare se un dato tipo soddisfa una specifica interfaccia o condizione. In altre parole, garantisce che un tipo abbia tutte le proprietà e i metodi richiesti da una specifica interfaccia. È un modo per garantire che una variabile rientri nella definizione di un tipo. Ecco un esempio: ```typescript type Columns = 'name' | 'nickName' | 'attributes'; type User = Record; // Annotazione del tipo tramite `User` const user: User = { name: 'Simone', nickName: undefined, attributes: ['dev', 'admin'], }; // Nelle righe seguenti, TypeScript non sarà in grado di dedurre correttamente user.attributes?.map(console.log); // La proprietà 'map' non esiste sul tipo 'string | string[]'. La proprietà 'map' non esiste sul tipo 'string'. user.nickName; // string | string[] | undefined // Asserzione di tipo tramite `as` const user2 = { name: 'Simon', nickName: undefined, attributes: ['dev', 'admin'], } as User; // Anche in questo caso, TypeScript non sarà in grado di dedurre correttamente user2.attributes?.map(console.log); // La proprietà 'map' non esiste sul tipo 'string | string[]'. La proprietà 'map' non esiste sul tipo 'string'. user2.nickName; // string | string[] | undefined // Utilizzando gli operatori `satisfies` ora possiamo dedurre correttamente i tipi const user3 = { name: 'Simon', nickName: undefined, attributes: ['dev', 'admin'], } satisfies User; user3.attributes?.map(console.log); // TypeScript deduce correttamente: string[] user3.nickName; // TypeScript deduce correttamente: undefined ``` ### Importazioni ed esportazioni solo per tipo Le importazioni ed esportazioni solo per tipo consentono di importare o esportare tipi senza importare o esportare i valori o le funzioni associati a tali tipi. Questo può essere utile per ridurre le dimensioni del bundle. Per utilizzare le importazioni solo per tipo, è possibile utilizzare la parola chiave `import type`. TypeScript consente l'utilizzo di estensioni di file sia di dichiarazione che di implementazione (.ts, .mts, .cts e .tsx) nelle importazioni solo tipo, indipendentemente dalle impostazioni `allowImportingTsExtensions`. Ad esempio: ```typescript import type { House } from './house.ts'; ``` Sono supportati i seguenti formati: ```typescript import type T from './mod'; import type { A, B } from './mod'; import type * as Types from './mod'; export type { T }; export type { T } from './mod'; ``` ### Dichiarazione using e Gestione Risorse Esplicita Una dichiarazione `using` è un binding immutabile con ambito a blocco, simile a `const`, utilizzato per la gestione delle risorse usa e getta. Quando inizializzato con un valore, il metodo `Symbol.dispose` di quel valore viene registrato e successivamente eseguito all'uscita dall'ambito del blocco che lo racchiude. Questo si basa sulla funzionalità di Gestione Risorse di ECMAScript, utile per eseguire attività di pulizia essenziali dopo la creazione di oggetti, come la chiusura di connessioni, l'eliminazione di file e il rilascio di memoria. Note: * A causa della sua recente introduzione nella versione 5.2 di TypeScript, la maggior parte dei runtime non dispone di supporto nativo. Sono necessari polyfill per: `Symbol.dispose`, `Symbol.asyncDispose`, `DisposableStack`, `AsyncDisposableStack`, `SuppressedError`. \* Inoltre, dovrai configurare il tuo file tsconfig.json come segue: ```json { "compilerOptions": { "target": "es2022", "lib": ["es2022", "esnext.disposable", "dom"] } } ``` Esempio: ```typescript //@ts-ignore Symbol.dispose ??= Symbol('Symbol.dispose'); // Simple polyfill const doWork = (): Disposable => { return { [Symbol.dispose]: () => { console.log('disposed'); }, }; }; console.log(1); { using work = doWork(); // La risorsa è dichiarata console.log(2); } // La risorsa viene eliminata (ad esempio, viene valutata `work[Symbol.dispose]()`) console.log(3); ``` Il codice registrerà: ```shell 1 2 disposed 3 ``` Una risorsa idonea per l'eliminazione deve rispettare l'interfaccia `Disposable`: ```typescript // lib.esnext.disposable.d.ts interface Disposable { [Symbol.dispose](): void; } ``` Le dichiarazioni `using` registrano le operazioni di eliminazione delle risorse in uno stack, assicurandosi che vengano eliminate nell'ordine inverso rispetto alla dichiarazione: ```typescript { using j = getA(), y = getB(); using k = getC(); } // elimina `C`, poi `B`, poi `A`. ``` È garantito che le risorse vengano eliminate, anche se si verificano codice o eccezioni successive. Questo potrebbe portare alla generazione di un'eccezione durante l'eliminazione, con la possibile soppressione di un'altra. Per conservare le informazioni sugli errori soppressi, è stata introdotta una nuova eccezione nativa, `SuppressedError`. #### dichiarazione await using Una dichiarazione `await using` gestisce una risorsa eliminabile in modo asincrono. Il valore deve avere un metodo `Symbol.asyncDispose`, che verrà atteso alla fine del blocco. ```typescript async function doWorkAsync() { await using work = doWorkAsync(); // La risorsa viene dichiarata } // La risorsa viene eliminata (ad esempio, viene valutata `await work[Symbol.asyncDispose]()`) ``` Per una risorsa eliminabile in modo asincrono, deve aderire all'interfaccia `Disposable` o `AsyncDisposable`: ```typescript // lib.esnext.disposable.d.ts interface AsyncDisposable { [Symbol.asyncDispose](): Promise; } ``` ```typescript //@ts-ignore Symbol.asyncDispose ??= Symbol('Symbol.asyncDispose'); // Simple polyfill class DatabaseConnection implements AsyncDisposable { // Un metodo che viene chiamato quando l'oggetto viene eliminato in modo asincrono [Symbol.asyncDispose]() { // Chiude la connessione e restituisce una promessa return this.close(); } async close() { console.log('Chiusura della connessione...'); await new Promise(resolve => setTimeout(resolve, 1000)); console.log('Connessione chiusa.'); } } async function doWork() { // Crea una nuova connessione e la elimina in modo asincrono quando esce dall'ambito await using connection = new DatabaseConnection(); // La risorsa viene dichiarata console.log('Sto lavorando...'); } // La risorsa viene eliminata (ad esempio, viene valutato `await connection[Symbol.asyncDispose]()`) doWork(); ``` Il codice registra: ```shell Sto lavorando... Chiusura della connessione... Connessione chiusa. ``` Le dichiarazioni `using` e `await using` sono consentite nelle istruzioni: `for`, `for-in`, `for-of`, `for-await-of`, `switch`. ### Attributi di importazione Gli attributi di importazione di TypeScript 5.3 (etichette per le importazioni) indicano al runtime come gestire i moduli (JSON, ecc.). Questo migliora la sicurezza garantendo importazioni chiare e si allinea con la Content Security Policy (CSP) per un caricamento più sicuro delle risorse. TypeScript garantisce che siano validi, ma lascia che sia il runtime a gestirne l'interpretazione per la gestione di moduli specifici. Esempio: ```typescript import config from './config.json' with { type: 'json' }; ``` con importazione dinamica: ```typescript const config = import('./config.json', { with: { type: 'json' } }); ``` ================================================ FILE: website/src/content/docs/it-it/book/overloads.md ================================================ --- title: Sovraccarichi sidebar: order: 51 label: 51. Sovraccarichi --- Gli overload di funzione in TypeScript consentono di definire più firme di funzione per un singolo nome di funzione, consentendo di definire funzioni che possono essere chiamate in più modi. Ecco un esempio: ```typescript // Sovraccarichi function sayHi(name: string): string; function sayHi(names: string[]): string[]; // Implementazione function sayHi(name: unknown): unknown { if (typeof name === 'string') { return `Ciao, ${name}!`; } else if (Array.isArray(name)) { return name.map(name => `Ciao, ${name}!`); } throw new Error('Valore non valido'); } sayHi('xx'); // Valido sayHi(['aa', 'bb']); // Valido ``` Ecco un altro esempio di utilizzo di overload di funzione all'interno di una `class`: ```typescript class Greeter { message: string; constructor(message: string) { this.message = message; } // Sovraccarichi sayHi(name: string): string; sayHi(name: string[]): ReadonlyArray; // Implementazione sayHi(name: unknown): unknown { if (typeof name === 'string') { return `${this.message}, ${name}!`; } else if (Array.isArray(name)) { return name.map(name => `${this.message}, ${name}!`); } throw new Error('value is invalid'); } } console.log(new Greeter('Hello').sayHi('Simon')); ``` ================================================ FILE: website/src/content/docs/it-it/book/predefined-conditional-types.md ================================================ --- title: Tipi Condizionali Predefiniti sidebar: order: 42 label: 42. Tipi Condizionali Predefiniti --- In TypeScript, i Tipi Condizionali Predefiniti sono tipi condizionali integrati forniti dal linguaggio. Sono progettati per eseguire trasformazioni di tipo comuni in base alle caratteristiche di un dato tipo. `Exclude`: questo tipo rimuove da Type tutti i tipi assegnabili a ExcludedType. `Extract`: questo tipo estrae da Union tutti i tipi assegnabili a Type. `NonNullable`: questo tipo rimuove null e undefined da Type. `ReturnType`: questo tipo estrae il tipo di ritorno di un Type di funzione. `Parameters`: questo tipo estrae i tipi di parametro di un Type di funzione. `Required`: Questo tipo rende obbligatorie tutte le proprietà in Type. `Partial`: Questo tipo rende facoltative tutte le proprietà in Type. `Readonly`: Questo tipo rende di sola lettura tutte le proprietà in Type. ================================================ FILE: website/src/content/docs/it-it/book/primitive-types.md ================================================ --- title: Tipi primitivi sidebar: order: 10 label: 10. Tipi primitivi --- TypeScript supporta 7 tipi primitivi. Un tipo di dati primitivo si riferisce a un tipo che non è un oggetto e non ha metodi associati. In TypeScript, tutti i tipi primitivi sono immutabili, il che significa che i loro valori non possono essere modificati una volta assegnati. ### string Il tipo primitivo `string` memorizza dati testuali e il valore è sempre racchiuso tra virgolette doppie o singole. ```typescript const x: string = 'x'; const y: string = 'y'; ``` Le stringhe possono estendersi su più righe se racchiuse dal carattere di apice inverso (`): ```typescript let sentence: string = `xxx, yyy`; ``` ### boolean Il tipo di dati `boolean` in TypeScript memorizza un valore binario, `true` o `false`. ```typescript const isReady: boolean = true; ``` ### number Un tipo di dati `number` in TypeScript è rappresentato da un valore in virgola mobile a 64 bit. Un tipo di dati `number` può rappresentare numeri interi e frazioni. TypeScript supporta anche i sistemi di numerazione esadecimale, binario e ottale, ad esempio: ```typescript const decimal: number = 10; const hexadecimal: number = 0xa00d; // L'esadecimale inizia con 0x const binary: number = 0b1010; // Il binario inizia con 0b const octal: number = 0o633; // L'ottale inizia con 0o ``` ### bigInt Un `bigInt` rappresenta valori numerici molto grandi (253 – 1) e non possono essere rappresentati con un `number`. Un `bigInt` può essere creato chiamando la funzione integrata `BigInt()` o aggiungendo `n` alla fine di qualsiasi letterale numerico intero: ```typescript const x: bigint = BigInt(9007199254740991); const y: bigint = 9007199254740991n; ``` Note: * I valori `bigInt` non possono essere combinati con `number` e non possono essere utilizzati con `Math` integrato, devono essere forzati allo stesso tipo. * I valori `bigInt` sono disponibili solo se la configurazione di destinazione è ES2020 o superiore. ### Simbolo I simboli sono identificatori univoci che possono essere utilizzati come chiavi di proprietà negli oggetti per evitare conflitti di denominazione. ```typescript type Obj = { [sym: symbol]: number; }; const a = Symbol('a'); const b = Symbol('b'); let obj: Obj = {}; obj[a] = 123; obj[b] = 456; console.log(obj[a]); // 123 console.log(obj[b]); // 456 ``` ### null e undefined I tipi `null` e `undefined` rappresentano entrambi nessun valore o l'assenza di qualsiasi valore. Il tipo `undefined` indica che il valore non è assegnato o inizializzato o indica un'assenza involontaria di valore. Il tipo `null` indica che sappiamo che il campo non ha un valore, quindi il valore non è disponibile, e indica un'assenza intenzionale di valore. ### Array Un `array` è un tipo di dati che può memorizzare più valori dello stesso tipo o meno. Può essere definito utilizzando la seguente sintassi: ```typescript const x: string[] = ['a', 'b']; const y: Array = ['a', 'b']; const j: Array = ['a', 1, 'b', 2]; // Unione ``` TypeScript supporta array di sola lettura utilizzando la seguente sintassi: ```typescript const x: readonly string[] = ['a', 'b']; // Modificatore di sola lettura const y: ReadonlyArray = ['a', 'b']; const j: ReadonlyArray = ['a', 1, 'b', 2]; j.push('x'); // Non valido ``` TypeScript supporta tuple e tuple di sola lettura: ```typescript const x: [string, number] = ['a', 1]; const y: readonly [string, number] = ['a', 1]; ``` ### any Il tipo di dati ``any` rappresenta letteralmente un valore "qualsiasi", ed è il valore predefinito quando TypeScript non può dedurre il tipo o non è specificato. Quando si utilizza `any`, il compilatore TypeScript salta il controllo del tipo, quindi non c'è sicurezza di tipo quando si utilizza `any`. In genere, non utilizzare `any` per silenziare il compilatore quando si verifica un errore, ma concentrarsi sulla correzione dell'errore, poiché utilizzando `any` è possibile interrompere i contratti e perdere i vantaggi del completamento automatico di TypeScript. Il tipo `any` potrebbe essere utile durante una migrazione graduale da JavaScript a TypeScript, in quanto può silenziare il compilatore. Per i nuovi progetti, utilizzare la configurazione TypeScript `noImplicitAny`, che consente a TypeScript di generare errori quando viene utilizzato o dedotto `any`. Il tipo `any` è solitamente fonte di errori che possono mascherare problemi reali con i tipi. Evitatelo il più possibile. ================================================ FILE: website/src/content/docs/it-it/book/readonly-properties.md ================================================ --- title: Proprietà di sola lettura sidebar: order: 13 label: 13. Proprietà di sola lettura --- È possibile impedire la scrittura su una proprietà utilizzando il modificatore `readonly`, che assicura che la proprietà non possa essere riscritta ma non fornisce alcuna garanzia di immutabilità totale: ```typescript interface Y { readonly a: number; } type X = { readonly a: number; }; type J = Readonly<{ a: number; }>; type K = { readonly [index: number]: string; }; ``` ================================================ FILE: website/src/content/docs/it-it/book/strictnullchecks.md ================================================ --- title: strictNullChecks sidebar: order: 18 label: 18. strictNullChecks --- `strictNullChecks` è un'opzione del compilatore TypeScript che impone un controllo null rigoroso. Quando questa opzione è abilitata, variabili e parametri possono essere assegnati a `null` o `undefined` solo se sono stati dichiarati esplicitamente di quel tipo utilizzando l'unione `null` | `undefined`. Se una variabile o un parametro non viene dichiarato esplicitamente come nullable, TypeScript genererà un errore per prevenire potenziali errori di runtime. ================================================ FILE: website/src/content/docs/it-it/book/symbols.md ================================================ --- title: Simboli sidebar: order: 58 label: 58. Simboli --- I simboli sono un tipo di dati primitivo che rappresenta un valore immutabile la cui unicità globale è garantita per tutta la durata del programma. I simboli possono essere utilizzati come chiavi per le proprietà degli oggetti e forniscono un modo per creare proprietà non enumerabili. ```typescript const key1: symbol = Symbol('key1'); const key2: symbol = Symbol('key2'); const obj = { [key1]: 'value 1', [key2]: 'value 2', }; console.log(obj[key1]); // valore 1 console.log(obj[key2]); // valore 2 ``` In WeakMaps e WeakSet, i simboli sono ora consentiti come chiavi. ================================================ FILE: website/src/content/docs/it-it/book/table-of-contents.md ================================================ --- title: Indice sidebar: order: 4 label: 4. Indice --- - The Concise TypeScript Book - Traduzioni - Download e sito web - Indice - Introduzione - Informazioni sull'autore - Introduzione a TypeScript - Cos'è TypeScript? - Perché TypeScript? - TypeScript e JavaScript - Generazione di codice TypeScript - JavaScript moderno ora (Downleveling) - Per iniziare con TypeScript - Installazione - Configurazione - File di configurazione TypeScript - target - lib - strict - module - moduleResolution - esModuleInterop - jsx - skipLibCheck - files - include - exclude - importHelpers - Consigli per la migrazione a TypeScript - Esplorazione del sistema di tipi - Il servizio di linguaggio TypeScript - Tipizzazione Strutturale - Regole fondamentali di confronto di TypeScript - Tipi come insiemi - Allargamento di tipo - Const - Modificatore Const sui parametri di tipo - Asserzione Const - Annotazione di tipo esplicita - Restringimento dei tipi - Condizioni - Generazione o restituzione - Unione Discriminata - Protezioni di tipo definite dall'utente - Tipi primitivi - string - Inferenza letterale - strictNullChecks - Enumerazioni - Enumerazioni numeriche - Enum String - Enum Constant - Mapping inverso - Enum ambientali - Membri calcolati e costanti - Restringimento - protezioni di tipo typeof - Restringimento di veridicità - Restringimento di uguaglianza - Restringimento dell'operatore "in" - Restringimento instanceof - Assegnazioni - Analisi del flusso di controllo - Tipo da Valore - Tipo da Ritorno Funzione - Tipo da modulo - Tipi mappati - Modificatori di tipo mappati - Tipi condizionali - Tipi condizionali distributivi - infer Inferenza di tipo nei tipi condizionali - Tipi Condizionali Predefiniti - Tipi di unione di template - Tipo Any - Tipo Unknown - Tipo Void - Tipo Never - Interfaccia e tipo - Sintassi comune - Tipi di base - Oggetti e interfacce - Modificatori di accesso - Get e Set - Accessori automatici nelle classi - this - Proprietà dei parametri - Classi astratte - Con i generici - Decoratori - Decoratori di classe - Decoratore di proprietà - Ereditarietà - Statiche - Inizializzazione delle proprietà - Sovraccarico dei metodi - Generici - Tipo generico - Classi generiche - Vincoli generici - Restringimento contestuale generico - Tipi strutturali cancellati - Namespace - Simboli - Direttive con tripla barra - Manipolazione dei tipi - Creazione di tipi da tipi - Tipi di accesso indicizzati - Tipi di utilità - Awaited\ - Partial\ - Required\ - Readonly\ - Record\ - Pick\ - Omit\ - Exclude\ - Extract\ - NonNullable\ - Parameters\ - ConstructorParameters\ - ReturnType\ - InstanceType\ - ThisParameterType\ - OmitThisParameter\ - ThisType\ - Uppercase\ - Lowercase\ - Capitalize\ - Uncapitalize\ - NoInfer\ - Altri - Gestione degli errori e delle eccezioni - Classi Mixin - Funzionalità del linguaggio asincrono - Iteratori e Generatori - Riferimento JSDoc di TsDocs - @types - JSX - Moduli ES6 - Operatore di elevamento a potenza ES7 - L'istruzione for-await-of - Nuova meta-proprietà target - Espressioni di importazione dinamica - "tsc –watch" - Operatore di asserzione non nullo - Dichiarazioni predefinite - Concatenamento opzionale - Operatore di coalescenza nullo - Tipi letterali di template - Sovraccarico di funzioni - Tipi ricorsivi - Tipi condizionali ricorsivi - Supporto per i moduli ECMAScript in Node - Funzioni di asserzione - Tipi di tupla variadici - Tipi boxed - Covarianza e Controvarianza in TypeScript - Annotazioni di varianza opzionali per i parametri di tipo - Firme di indice con pattern di stringhe modello - Operatore `satisfies` - Importazioni ed esportazioni solo per tipo - Dichiarazione using e Gestione Risorse Esplicita - dichiarazione await using - Attributi di importazione ================================================ FILE: website/src/content/docs/it-it/book/template-union-types.md ================================================ --- title: Tipi di unione di template sidebar: order: 43 label: 43. Tipi di unione di template --- I tipi di unione di template possono essere utilizzati per unire e manipolare il testo all'interno del sistema di tipi, ad esempio: ```typescript type Status = 'active' | 'inactive'; type Products = 'p1' | 'p2'; type ProductId = `id-${Products}-${Status}`; // "id-p1-active" | "id-p1-inactive" | "id-p2-active" | "id-p2-inactive" ``` ================================================ FILE: website/src/content/docs/it-it/book/the-concise-typescript-book.md ================================================ --- title: The Concise TypeScript Book sidebar: order: 1 label: 1. The Concise TypeScript Book --- The Concise TypeScript Book offre una panoramica completa e concisa delle funzionalità di TypeScript. Questo libro offre spiegazioni chiare che coprono tutti gli aspetti dell'ultima versione del linguaggio, dal suo potente sistema di tipi alle funzionalità avanzate. Che siate principianti o sviluppatori esperti, questo libro è una risorsa preziosa per migliorare la vostra comprensione e competenza in TypeScript. Questo libro è completamente gratuito e open source. Credo che un'istruzione tecnica di alta qualità debba essere accessibile a tutti, ed è per questo che mantengo questo libro gratuito e aperto. Se il libro ti ha aiutato a risolvere un bug, a comprendere un concetto ostico o a progredire nella tua carriera, ti prego di considerare di sostenere il mio lavoro pagando quanto vuoi (prezzo suggerito: 15 euro) o sponsorizzando un caffè. Il tuo supporto mi aiuta a mantenere i contenuti aggiornati e ad ampliarli con nuovi esempi e spiegazioni più approfondite. [![Buy Me a Coffee](https://img.shields.io/badge/buy_me_a_coffee-FFDD00?style=for-the-badge&logo=buy-me-a-coffee&logoColor=black)](https://www.buymeacoffee.com/simonepoggiali) [![Dona](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/donate/?business=QW82ZS956XLFY&no_recurring=0¤cy_code=EUR) ================================================ FILE: website/src/content/docs/it-it/book/the-never-type.md ================================================ --- title: Il tipo never sidebar: order: 25 label: 25. Il tipo never --- Quando una variabile viene ristretta a un tipo che non può contenere alcun valore, il compilatore TypeScript dedurrà che la variabile deve essere del tipo `never`. Questo perché il tipo `never` rappresenta un valore che non può mai essere prodotto. ```typescript const printValue = (val: string | number) => { if (typeof val === 'string') { console.log(val.toUpperCase()); } else if (typeof val === 'number') { console.log(val.toFixed(2)); } else { // val ha il tipo never qui perché non può essere altro che una stringa o un numero const neverVal: never = val; console.log(`Valore imprevisto: ${neverVal}`); } }; ``` ================================================ FILE: website/src/content/docs/it-it/book/translations.md ================================================ --- title: Traduzioni sidebar: order: 2 label: 2. Traduzioni --- Questo libro è stato tradotto in diverse lingue, tra cui: [Cinese](https://github.com/gibbok/typescript-book/blob/main/README-zh_CN.md) [Italiano](https://github.com/gibbok/typescript-book/blob/main/README-it_IT.md) [Portoghese (Brasile)](https://github.com/gibbok/typescript-book/blob/main/README-pt_BR.md) [Svedese](https://github.com/gibbok/typescript-book/blob/main/README-sv_SE.md) ================================================ FILE: website/src/content/docs/it-it/book/triple-slash-directives.md ================================================ --- title: Direttive con tripla barra sidebar: order: 59 label: 59. Direttive con tripla barra --- Le direttive con tripla barra sono commenti speciali che forniscono istruzioni al compilatore su come elaborare un file. Queste direttive iniziano con tre barre consecutive (`///`) e sono in genere posizionate all'inizio di un file TypeScript e non hanno alcun effetto sul comportamento in fase di esecuzione. Le direttive con tripla barra vengono utilizzate per fare riferimento a dipendenze esterne, specificare il comportamento di caricamento dei moduli, abilitare/disabilitare determinate funzionalità del compilatore e altro ancora. Alcuni esempi: Riferimento a un file di dichiarazione: ```typescript /// ``` Indicare il formato del modulo: ```typescript /// ``` Abilitare le opzioni del compilatore, nell'esempio seguente, in modalità strict: ```typescript /// ``` ================================================ FILE: website/src/content/docs/it-it/book/tuple-type-anonymous.md ================================================ --- title: Tipo di tupla (anonimo) sidebar: order: 28 label: 28. Tipo di tupla (anonimo) --- Un tipo di tupla è un tipo che rappresenta un array con un numero fisso di elementi e i relativi tipi. Un tipo di tupla impone un numero specifico di elementi e i rispettivi tipi in un ordine fisso. I tipi di tupla sono utili quando si desidera rappresentare una raccolta di valori con tipi specifici, dove la posizione di ciascun elemento nell'array ha un significato specifico. ```typescript type Point = [number, number]; ``` ================================================ FILE: website/src/content/docs/it-it/book/type-annotations.md ================================================ --- title: Annotazioni di tipo sidebar: order: 11 label: 11. Annotazioni di tipo --- Sulle variabili dichiarate usando `var`, `let` e `const`, è possibile aggiungere facoltativamente un tipo: ```typescript const x: number = 1; ``` TypeScript esegue un buon lavoro nell'inferenza dei tipi, soprattutto quando si tratta di tipi semplici, quindi queste dichiarazioni nella maggior parte dei casi non sono necessarie. Sulle funzioni è possibile aggiungere annotazioni di tipo ai parametri: ```typescript function sum(a: number, b: number) { return a + b; } ``` Il seguente è un esempio che utilizza una funzione anonima (la cosiddetta funzione lambda): ```typescript const sum = (a: number, b: number) => a + b; ``` Queste annotazioni possono essere evitate quando è presente un valore predefinito per un parametro: ```typescript const sum = (a = 10, b: number) => a + b; ``` Le annotazioni del tipo di ritorno possono essere aggiunte alle funzioni: ```typescript const sum = (a = 10, b: number): number => a + b; ``` Questo è utile soprattutto per le funzioni più complesse, poiché scrivere esplicitamente il tipo di ritorno prima di un'implementazione può aiutare a pensare meglio alla funzione. In genere, si consiglia di annotare le firme dei tipi, ma non le variabili locali del corpo, e di aggiungere i tipi sempre ai letterali degli oggetti. ================================================ FILE: website/src/content/docs/it-it/book/type-from-func-return.md ================================================ --- title: Tipo da Ritorno Funzione sidebar: order: 35 label: 35. Tipo da Ritorno Funzione --- Il tipo da Ritorno Funzione si riferisce alla possibilità di inferire automaticamente il tipo di ritorno di una funzione in base alla sua implementazione. Ciò consente a TypeScript di determinare il tipo del valore restituito dalla funzione senza annotazioni di tipo esplicite. ```typescript const add = (x: number, y: number) => x + y; // TypeScript può dedurre che il tipo restituito dalla funzione sia un numero ``` ================================================ FILE: website/src/content/docs/it-it/book/type-from-module.md ================================================ --- title: Tipo da modulo sidebar: order: 36 label: 36. Tipo da modulo --- Il tipo da modulo si riferisce alla possibilità di utilizzare i valori esportati di un modulo per dedurne automaticamente il tipo. Quando un modulo esporta un valore con un tipo specifico, TypeScript può utilizzare tali informazioni per dedurre automaticamente il tipo di quel valore quando viene importato in un altro modulo. ```typescript // calc.ts export const add = (x: number, y: number) => x + y; // index.ts import { add } from 'calc'; const r = add(1, 2); // r è un numero ``` ================================================ FILE: website/src/content/docs/it-it/book/type-from-value.md ================================================ --- title: Tipo da Valore sidebar: order: 34 label: 34. Tipo da Valore --- In TypeScript, il tipo da valore si riferisce all'inferenza automatica di un tipo da un valore o da un'espressione tramite inferenza di tipo. ```typescript const x = 'x'; // TypeScript inferisce 'x' come una stringa letterale con 'const' (immutabile), ma lo amplia a 'string' con 'let' (riassegnabile). ``` ================================================ FILE: website/src/content/docs/it-it/book/type-indexing.md ================================================ --- title: Indicizzazione dei tipi sidebar: order: 33 label: 33. Indicizzazione dei tipi --- L'indicizzazione dei tipi si riferisce alla capacità di definire tipi che possono essere indicizzati da una chiave non nota in anticipo, utilizzando una firma di indice per specificare il tipo per le proprietà che non sono dichiarate esplicitamente. ```typescript type Dictionary = { [key: string]: T; }; const myDict: Dictionary = { a: 'a', b: 'b' }; console.log(myDict['a']); // Restituisce un ``` ================================================ FILE: website/src/content/docs/it-it/book/type-manipulation.md ================================================ --- title: Manipolazione dei tipi sidebar: order: 60 label: 60. Manipolazione dei tipi --- ### Creazione di tipi da tipi È possibile creare nuovi tipi componendo, manipolando o trasformando tipi esistenti. Tipi di intersezione (`&`): Consentono di combinare più tipi in un unico tipo: ```typescript type A = { foo: number }; type B = { bar: string }; type C = A & B; // Intersezione di A e B const obj: C = { foo: 42, bar: 'hello' }; ``` Tipi di unione (`|`): Consente di definire un tipo che può essere di diversi tipi: ```typescript type Result = string | number; const value1: Result = 'hello'; const value2: Result = 42; ``` Tipi mappati: Consentono di trasformare le proprietà di un tipo esistente per crearne uno nuovo: ```typescript type Mutable = { readonly [P in keyof T]: T[P]; }; type Person = { name: string; age: number; }; type ImmutablePerson = Mutable; // Le proprietà diventano di sola lettura ``` Tipi condizionali: Consentono di creare tipi in base ad alcune condizioni: ```typescript type ExtractParam = T extends (param: infer P) => any ? P : never; type MyFunction = (name: string) => number; type ParamType = ExtractParam; // string ``` ### Tipi di accesso indicizzati In TypeScript è possibile accedere e manipolare i tipi di proprietà all'interno di un altro tipo utilizzando un indice, `Type[Key]`. ```typescript type Person = { name: string; age: number; }; type AgeType = Person['age']; // number ``` ```typescript type MyTuple = [string, number, boolean]; type MyType = MyTuple[2]; // boolean ``` ### Tipi di utilità Diversi tipi di utilità predefiniti possono essere utilizzati per manipolare i tipi; di seguito è riportato un elenco dei più comuni: #### Awaited\ Costruisce un tipo che esegue ricorsivamente l'unwrapping dei tipi Promise. ```typescript type A = Awaited>; // string ``` #### Partial\ Costruisce un tipo con tutte le proprietà di T impostate su optional. ```typescript type Person = { name: string; age: number; }; type A = Partial; // { name?: string | undefined; age?:number | undefined; } ``` #### Required\ Costruisce un tipo con tutte le proprietà di T impostate su required. ```typescript type Person = { name?: string; age?: number; }; type A = Required; // { name: string; age:number; } ``` #### Readonly\ Costruisce un tipo con tutte le proprietà di T impostate su readonly. ```typescript type Person = { name: string; age: number; }; type A = Readonly; const a: A = { name: 'Simon', age: 17 }; a.name = 'John'; // Non valido ``` #### Record\ Costruisce un tipo con un insieme di proprietà K di tipo T. ```typescript type Product = { name: string; price: number; }; const products: Record = { apple: { name: 'Apple', price: 0.5 }, banana: { name: 'Banana', price: 0.25 }, }; console.log(products.apple); // { name: 'Apple', price: 0.5 } ``` #### Pick\ Costruisce un tipo selezionando le proprietà specificate K da T. ```typescript type Product = { name: string; price: number; }; type Price = Pick; // { price: number; } ``` #### Omit\ Costruisce un tipo omettendo le proprietà specificate K da T. ```typescript type Product = { name: string; price: number; }; type Name = Omit; // { name: string; } ``` #### Exclude\ Costruisce un tipo escludendo tutti i valori di tipo U da T. ```typescript type Union = 'a' | 'b' | 'c'; type MyType = Exclude; // b ``` #### Extract\ Costruisce un tipo estraendo tutti i valori di tipo U da T. ```typescript type Union = 'a' | 'b' | 'c'; type MyType = Extract; // a | c ``` #### NonNullable\ Costruisce un tipo escludendo null e undefined da T. ```typescript type Union = 'a' | null | undefined | 'b'; type MyType = NonNullable; // 'a' | 'b' ``` #### Parameters\ Estrae i tipi di parametro di una funzione di tipo T. ```typescript type Func = (a: string, b: number) => void; type MyType = Parameters; // [a: string, b: number] ``` #### ConstructorParameters\ Estrae i tipi di parametro di una funzione costruttore di tipo T. ```typescript class Person { constructor( public name: string, public age: number ) {} } type PersonConstructorParams = ConstructorParameters; // [name: string, age: number] const params: PersonConstructorParams = ['John', 30]; const person = new Person(...params); console.log(person); // Person { name: 'John', age: 30 } ``` #### ReturnType\ Estrae il tipo di ritorno di una funzione di tipo T. ```typescript type Func = (name: string) => number; type MyType = ReturnType; // number ``` #### InstanceType\ Estrae il tipo di istanza di una classe di tipo T. ```typescript class Person { name: string; constructor(name: string) { this.name = name; } sayHello() { console.log(`Ciao, mi chiamo ${this.name}!`); } } type PersonInstance = InstanceType; const person: PersonInstance = new Person('John'); person.sayHello(); // Ciao, mi chiamo John! ``` #### ThisParameterType\ Estrae il tipo del parametro 'this' da una funzione di tipo T. ```typescript interface Person { name: string; greet(this: Person): void; } type PersonThisType = ThisParameterType; // Person ``` #### OmitThisParameter\ Rimuove il parametro 'this' da una funzione di tipo T. ```typescript function capitalize(this: String) { return this[0].toUpperCase + this.substring(1).toLowerCase(); } type CapitalizeType = OmitThisParameter; // () => string ``` #### ThisType\ Funge da marcatore per un tipo `this` contestuale. ```typescript type Logger = { log: (error: string) => void; }; let helperFunctions: { [name: string]: Function } & ThisType = { hello: function () { this.log('some error'); // Valido poiché "log" è parte di "this". this.update(); // Non valido }, }; ``` #### Uppercase\ Rendi maiuscolo il nome del tipo di input T. ```typescript type MyType = Uppercase<'abc'>; // "ABC" ``` #### Lowercase\ Rendi minuscolo il nome del tipo di input T. ```typescript type MyType = Lowercase<'ABC'>; // "abc" ``` #### Capitalize\ Inserisci in maiuscolo il nome del tipo di input T. ```typescript type MyType = Capitalize<'abc'>; // "Abc" ``` #### Uncapitalize\ Inserisci in maiuscolo il nome del tipo di input T. ```typescript type MyType = Uncapitalize<'Abc'>; // "abc" ``` #### NoInfer\ NoInfer è un tipo di utilità progettato per bloccare l'inferenza automatica dei tipi nell'ambito di una funzione generica. Esempio: ```typescript // Inferenza automatica dei tipi nell'ambito di una funzione generica. function fn(x: T[], y: T) { return x.concat(y); } const r = fn(['a', 'b'], 'c'); // Il tipo qui è ("a" | "b" | "c")[] ``` Con NoInfer: ```typescript // Funzione di esempio che utilizza NoInfer per impedire l'inferenza di tipo function fn2(x: T[], y: NoInfer) { return x.concat(y); } const r2 = fn2(['a', 'b'], 'c'); // Errore: l'argomento di tipo '"c"' non è assegnabile al parametro di tipo '"a" | "b"'. ``` ================================================ FILE: website/src/content/docs/it-it/book/type-predicates.md ================================================ --- title: Predicati di tipo sidebar: order: 23 label: 23. Predicati di tipo --- I predicati di tipo in TypeScript sono funzioni che restituiscono un valore booleano e vengono utilizzate per restringere il tipo di una variabile a un tipo più specifico. ```typescript const isString = (value: unknown): value is string => typeof value === 'string'; const foo = (bar: unknown) => { if (isString(bar)) { console.log(bar.toUpperCase()); } else { console.log('not a string'); } }; ``` ================================================ FILE: website/src/content/docs/it-it/book/typescript-introduction.md ================================================ --- title: Introduzione a TypeScript sidebar: order: 7 label: 7. Introduzione a TypeScript --- ### Cos'è TypeScript? TypeScript è un linguaggio di programmazione fortemente tipizzato basato su JavaScript. È stato originariamente progettato da Anders Hejlsberg nel 2012 ed è attualmente sviluppato e gestito da Microsoft come progetto open source. TypeScript si compila in JavaScript e può essere eseguito in qualsiasi runtime JavaScript (ad esempio, un browser o Node.js su un server). Supporta diversi paradigmi di programmazione, come la programmazione funzionale, generica, imperativa e orientata agli oggetti, ed è un linguaggio compilato (transpilato) che viene convertito in JavaScript prima dell'esecuzione. ### Perché TypeScript? TypeScript è un linguaggio fortemente tipizzato che aiuta a prevenire errori di programmazione comuni ed evitare determinati tipi di errori di runtime prima dell'esecuzione del programma. Un linguaggio fortemente tipizzato consente allo sviluppatore di specificare vari vincoli e comportamenti del programma nelle definizioni dei tipi di dati, facilitando la verifica della correttezza del software e la prevenzione dei difetti. Questo è particolarmente utile nelle applicazioni su larga scala. Alcuni dei vantaggi di TypeScript: * Tipizzazione statica, facoltativamente fortemente tipizzata * Inferenza di tipo * Accesso alle funzionalità di ES6 ed ES7 * Compatibilità multipiattaforma e multibrowser * Supporto degli strumenti con IntelliSense ### TypeScript e JavaScript TypeScript è scritto in file `.ts` o `.tsx`, mentre i file JavaScript sono scritti in file `.js` o `.jsx`. I file con estensione `.tsx` o `.jsx` possono contenere l'estensione di sintassi JavaScript JSX, utilizzata in React per lo sviluppo dell'interfaccia utente. TypeScript è un superset tipizzato di JavaScript (ECMAScript 2015) in termini di sintassi. Tutto il codice JavaScript è codice TypeScript valido, ma il contrario non è sempre vero. Ad esempio, si consideri una funzione in un file JavaScript con estensione `.js`, come la seguente: ```typescript const sum = (a, b) => a + b; ``` La funzione può essere convertita e utilizzata in TypeScript modificando l'estensione del file in `.ts`. Tuttavia, se la stessa funzione è annotata con tipi TypeScript, non può essere eseguita in alcun runtime JavaScript senza compilazione. Il seguente codice TypeScript genererà un errore di sintassi se non compilato: ```typescript const sum = (a: number, b: number): number => a + b; ``` TypeScript è stato progettato per rilevare possibili eccezioni che possono verificarsi in fase di runtime durante la compilazione, consentendo allo sviluppatore di definire l'intento con annotazioni di tipo. Inoltre, TypeScript può anche rilevare problemi se non viene fornita alcuna annotazione di tipo. Ad esempio, il seguente frammento di codice non specifica alcun tipo TypeScript: ```typescript const items = [{ x: 1 }, { x: 2 }]; const result = items.filter(item => item.y); ``` In questo caso, TypeScript rileva un errore e segnala: ```text La proprietà 'y' non esiste sul tipo '{ x: number; }'. ``` Il sistema di tipi di TypeScript è ampiamente influenzato dal comportamento runtime di JavaScript. Ad esempio, l'operatore di addizione (+), che in JavaScript può eseguire sia la concatenazione di stringhe che l'addizione numerica, è modellato allo stesso modo in TypeScript: ```typescript const result = '1' + 1; // Il risultato è di tipo stringa ``` Il team di TypeScript ha deliberatamente deciso di segnalare come errori l'utilizzo insolito di JavaScript. Ad esempio, si consideri il seguente codice JavaScript valido: ```typescript const result = 1 + true; // In JavaScript, il risultato è uguale a 2 ``` Tuttavia, TypeScript genera un errore: ```text L'operatore '+' non può essere applicato ai tipi 'number' e 'boolean'. ``` Questo errore si verifica perché TypeScript applica rigorosamente la compatibilità di tipo e, in questo caso, identifica un'operazione non valida tra un numero e un valore booleano. ### Generazione di codice TypeScript Il compilatore TypeScript ha due responsabilità principali: il controllo degli errori di tipo e la compilazione in JavaScript. Questi due processi sono indipendenti l'uno dall'altro. I tipi non influenzano l'esecuzione del codice in un runtime JavaScript, poiché vengono completamente cancellati durante la compilazione. TypeScript può comunque generare codice JavaScript anche in presenza di errori di tipo. Ecco un esempio di codice TypeScript con un errore di tipo: ```typescript const add = (a: number, b: number): number => a + b; const result = add('x', 'y'); // L'argomento di tipo 'string' non è assegnabile al parametro di tipo 'number'. ``` Tuttavia, può comunque produrre un output JavaScript eseguibile: ```typescript 'use strict'; const add = (a, b) => a + b; const result = add('x', 'y'); // xy ``` Non è possibile controllare i tipi TypeScript in fase di esecuzione. Ad esempio: ```typescript interface Animal { name: string; } interface Dog extends Animal { bark: () => void; } interface Cat extends Animal { meow: () => void; } const makeNoise = (animal: Animal) => { if (animal instanceof Dog) { // 'Dog' si riferisce solo a un tipo, ma qui viene utilizzato come valore. // ... } }; ``` Poiché i tipi vengono cancellati dopo la compilazione, non è possibile eseguire questo codice in JavaScript. Per riconoscere i tipi a runtime, dobbiamo usare un altro meccanismo. TypeScript offre diverse opzioni, una delle quali è la "tagged union". Ad esempio: ```typescript interface Dog { kind: 'dog'; // Tagged union bark: () => void; } interface Cat { kind: 'cat'; // Tagged union meow: () => void; } type Animal = Dog | Cat; const makeNoise = (animal: Animal) => { if (animal.kind === 'dog') { animal.bark(); } else { animal.meow(); } }; const dog: Dog = { kind: 'dog', bark: () => console.log('bark'), }; makeNoise(dog); ``` La proprietà "kind" è un valore che può essere utilizzato in fase di esecuzione per distinguere gli oggetti in JavaScript. È anche possibile che un valore in fase di esecuzione abbia un tipo diverso da quello dichiarato nella dichiarazione di tipo. Ad esempio, se lo sviluppatore ha interpretato erroneamente un tipo API e lo ha annotato in modo errato. TypeScript è un superset di JavaScript, quindi la parola chiave "class" può essere utilizzata come tipo e valore in fase di esecuzione. ```typescript class Animal { constructor(public name: string) {} } class Dog extends Animal { constructor( public name: string, public bark: () => void ) { super(name); } } class Cat extends Animal { constructor( public name: string, public meow: () => void ) { super(name); } } type Mammal = Dog | Cat; const makeNoise = (mammal: Mammal) => { if (mammal instanceof Dog) { mammal.bark(); } else { mammal.meow(); } }; const dog = new Dog('Fido', () => console.log('bark')); makeNoise(dog); ``` In JavaScript, una "classe" ha una proprietà "prototype" e l'operatore "instanceof" può essere utilizzato per verificare se la proprietà prototype di un costruttore appare in qualsiasi punto della catena di prototipi di un oggetto. TypeScript non ha alcun effetto sulle prestazioni di runtime, poiché tutti i tipi verranno cancellati. Tuttavia, TypeScript introduce un certo overhead in fase di compilazione. ### JavaScript moderno ora (Downleveling) TypeScript può compilare codice per qualsiasi versione rilasciata di JavaScript a partire da ECMAScript 3 (1999). Ciò significa che TypeScript può transpilare codice dalle funzionalità JavaScript più recenti a versioni precedenti, un processo noto come Downleveling. Questo consente l'utilizzo di JavaScript moderno mantenendo la massima compatibilità con gli ambienti di runtime più vecchi. È importante notare che durante la transpilazione a una versione precedente di JavaScript, TypeScript potrebbe generare codice che potrebbe comportare un sovraccarico di prestazioni rispetto alle implementazioni native. Ecco alcune delle funzionalità di JavaScript moderno che possono essere utilizzate in TypeScript: * Moduli ECMAScript al posto delle callback "define" in stile AMD o delle istruzioni "require" di CommonJS. * Classi al posto dei prototipi. * Dichiarazione di variabili utilizzando "let" o "const" al posto di "var". * Ciclo "for-of" o ".forEach" al posto del tradizionale ciclo "for". * Funzioni freccia al posto delle espressioni di funzione. * Assegnazione destrutturata. \* Nomi abbreviati di proprietà/metodi e nomi di proprietà calcolate. * Parametri di funzione predefiniti. Sfruttando queste moderne funzionalità di JavaScript, gli sviluppatori possono scrivere codice più espressivo e conciso in TypeScript. ================================================ FILE: website/src/content/docs/it-it/book/union-type.md ================================================ --- title: Tipo Unione sidebar: order: 31 label: 31. Tipo Unione --- Un Tipo Unione è un tipo che rappresenta un valore che può essere di diversi tipi. I Tipi Unione sono indicati con il simbolo `|` tra ogni tipo possibile. ```typescript let x: string | number; x = 'hello'; // Valido x = 123; // Valido ``` ================================================ FILE: website/src/content/docs/it-it/book/unknown-type.md ================================================ --- title: Tipo Unknown sidebar: order: 45 label: 45. Tipo Unknown --- In TypeScript, il tipo `unknown` rappresenta un valore di tipo sconosciuto. A differenza del tipo `any`, che consente qualsiasi tipo di valore, `unknown` richiede un controllo o un'asserzione di tipo prima di poter essere utilizzato in un modo specifico, quindi non sono consentite operazioni su un `unknown` senza prima aver effettuato un'asserzione o aver limitato il campo a un tipo più specifico. Il tipo `unknown` è assegnabile solo a qualsiasi tipo e il tipo `unknown` stesso è un'alternativa type-safe ad `any`. ```typescript let value: unknown; let value1: unknown = value; // Valido let value2: any = value; // Valido let value3: boolean = value; // Non valido let value4: number = value; // Non valido ``` ```typescript const add = (a: unknown, b: unknown): number | undefined => typeof a === 'number' && typeof b === 'number' ? a + b : undefined; console.log(add(1, 2)); // 3 console.log(add('x', 2)); // non definito ``` ================================================ FILE: website/src/content/docs/it-it/book/void-type.md ================================================ --- title: Tipo Void sidebar: order: 46 label: 46. Tipo Void --- Il tipo `void` viene utilizzato per indicare che una funzione non restituisce un valore. ```typescript const sayHello = (): void => { console.log('Hello!'); }; ``` ================================================ FILE: website/src/content/docs/it-it/index.mdx ================================================ --- title: TypeScript Book description: The Concise TypeScript Book template: splash hero: tagline: Il Concise TypeScript Book offre una panoramica completa e concisa delle funzionalità di TypeScript. Offre spiegazioni chiare che coprono tutti gli aspetti dell'ultima versione del linguaggio, dal suo potente sistema di tipi alle funzionalità avanzate. Che siate principianti o sviluppatori esperti, questo libro è una risorsa preziosa per migliorare la vostra comprensione e competenza in TypeScript.

Questo libro è completamente gratuito e open source. actions: - text: Leggi ora! link: /typescript-book/it-it/book/the-concise-typescript-book/ icon: right-arrow variant: primary - text: GitHub link: https://github.com/gibbok/typescript-book icon: github variant: secondary - text: X.com link: https://twitter.com/gibbok_coding icon: x.com variant: secondary --- ================================================ FILE: website/src/content/docs/pt-br/book/about-the-author.md ================================================ --- title: Sobre o Autor sidebar: order: 6 label: 6. Sobre o Autor --- Simone Poggiali é um Engenheiro Staff experiente com paixão por escrever código de nível profissional desde os anos 90. Ao longo de sua carreira internacional, contribuiu para inúmeros projetos para uma ampla gama de clientes, de startups a grandes organizações. Empresas notáveis como HelloFresh, Siemens, O2, Leroy Merlin e Snowplow se beneficiaram de sua expertise e dedicação. Você pode encontrar Simone Poggiali nas seguintes plataformas: * LinkedIn: [https://www.linkedin.com/in/simone-poggiali](https://www.linkedin.com/in/simone-poggiali) * GitHub: [https://github.com/gibbok](https://github.com/gibbok) * X.com: [https://x.com/gibbok_coding](https://x.com/gibbok_coding) * Email: gibbok.coding📧gmail.com Lista completa de colaboradores: [https://github.com/gibbok/typescript-book/graphs/contributors](https://github.com/gibbok/typescript-book/graphs/contributors) ================================================ FILE: website/src/content/docs/pt-br/book/any-type.md ================================================ --- title: Tipo any sidebar: order: 44 label: 44. Tipo any --- O tipo `any` é um tipo especial (supertipo universal) que pode ser usado para representar qualquer tipo de valor (primitivos, objetos, arrays, funções, erros, símbolos). É frequentemente usado em situações onde o tipo de um valor não é conhecido em tempo de compilação, ou ao trabalhar com valores de APIs externas ou bibliotecas que não possuem tipagens TypeScript. Ao utilizar o tipo `any`, você está indicando ao compilador TypeScript que os valores devem ser representados sem quaisquer limitações. Para maximizar a segurança de tipo em seu código, considere o seguinte: * Limite o uso de `any` a casos específicos onde o tipo é verdadeiramente desconhecido. * Não retorne tipos `any` de uma função, pois isso enfraquece a segurança de tipo no código que a utiliza. * Em vez de `any`, use `@ts-ignore` se precisar silenciar o compilador. ```typescript let value: any; value = true; // Válido value = 7; // Válido ``` ================================================ FILE: website/src/content/docs/pt-br/book/assignments.md ================================================ --- title: Atribuições sidebar: order: 21 label: 21. Atribuições --- O estreitamento do TypeScript usando atribuições é uma forma de estreitar o tipo de uma variável com base no valor atribuído a ela. Quando uma variável recebe um valor, o TypeScript infere seu tipo com base no valor atribuído e estreita o tipo da variável para corresponder ao tipo inferido. ```typescript let value: string | number; value = 'hello'; if (typeof value === 'string') { console.log(value.toUpperCase()); } value = 42; if (typeof value === 'number') { console.log(value.toFixed(2)); } ``` ================================================ FILE: website/src/content/docs/pt-br/book/built-in-type-primitives.md ================================================ --- title: Tipos Primitivos Integrados sidebar: order: 49 label: 49. Tipos Primitivos Integrados --- O TypeScript possui vários tipos primitivos integrados que podem ser usados para definir variáveis, parâmetros de função e tipos de retorno: * `number`: Representa valores numéricos, incluindo inteiros e números de ponto flutuante. * `string`: Representa dados textuais. * `boolean`: Representa valores lógicos, que podem ser true ou false. * `null`: Representa a ausência de um valor. * `undefined`: Representa um valor que não foi atribuído ou não foi definido. * `symbol`: Representa um identificador único. Symbols são normalmente usados como chaves para propriedades de objetos. * `bigint`: Representa inteiros de precisão arbitrária. * `any`: Representa um tipo dinâmico ou desconhecido. Variáveis do tipo any podem conter valores de qualquer tipo e ignoram a verificação de tipos. * `void`: Representa a ausência de qualquer tipo. É comumente usado como o tipo de retorno de funções que não retornam um valor. * `never`: Representa um tipo para valores que nunca ocorrem. É normalmente usado como o tipo de retorno de funções que lançam um erro ou entram em um loop infinito. ================================================ FILE: website/src/content/docs/pt-br/book/class.md ================================================ --- title: Classes sidebar: order: 54 label: 54. Classes --- ### Sintaxe Comum de Classes A palavra-chave `class` é usada no TypeScript para definir uma classe. Abaixo, você pode ver um exemplo: ```typescript class Person { private name: string; private age: number; constructor(name: string, age: number) { this.name = name; this.age = age; } public sayHi(): void { console.log( `Olá, meu nome é ${this.name} e eu tenho ${this.age} anos.` ); } } ``` A palavra-chave `class` é usada para definir uma classe chamada "Person". A classe possui duas propriedades privadas: name do tipo `string` e age do tipo `number`. O construtor é definido usando a palavra-chave `constructor`. Ele recebe name e age como parâmetros e os atribui às propriedades correspondentes. A classe possui um método `public` chamado `sayHi` que registra uma mensagem de saudação. Para criar uma instância de uma classe no TypeScript, você pode usar a palavra-chave `new` seguida pelo nome da classe, seguida de parênteses `()`. Por exemplo: ```typescript const myObject = new Person('John Doe', 25); myObject.sayHi(); // Saída: Olá, meu nome é John Doe e eu tenho 25 anos. ``` ### Construtor Os construtores são métodos especiais dentro de uma classe que são usados para inicializar as propriedades do objeto quando uma instância da classe é criada. ```typescript class Person { public name: string; public age: number; constructor(name: string, age: number) { this.name = name; this.age = age; } sayHello() { console.log( `Olá, meu nome é ${this.name} e eu tenho ${this.age} anos.` ); } } const john = new Person('Simon', 17); john.sayHello(); ``` É possível sobrecarregar um construtor usando a seguinte sintaxe: ```typescript type Sex = 'm' | 'f'; class Person { name: string; age: number; sex: Sex; constructor(name: string, age: number, sex?: Sex); constructor(name: string, age: number, sex: Sex) { this.name = name; this.age = age; this.sex = sex ?? 'm'; } } const p1 = new Person('Simon', 17); const p2 = new Person('Alice', 22, 'f'); ``` No TypeScript, é possível definir múltiplas sobrecargas de construtor, mas você pode ter apenas uma implementação que deve ser compatível com todas as sobrecargas, o que pode ser alcançado usando um parâmetro opcional. ```typescript class Person { name: string; age: number; constructor(); constructor(name: string); constructor(name: string, age: number); constructor(name?: string, age?: number) { this.name = name ?? 'Desconhecido'; this.age = age ?? 0; } displayInfo() { console.log(`Nome: ${this.name}, Idade: ${this.age}`); } } const person1 = new Person(); person1.displayInfo(); // Nome: Desconhecido, Idade: 0 const person2 = new Person('John'); person2.displayInfo(); // Nome: John, Idade: 0 const person3 = new Person('Jane', 25); person3.displayInfo(); // Nome: Jane, Idade: 25 ``` ### Construtores Privados e Protegidos No TypeScript, os construtores podem ser marcados como privados ou protegidos, o que restringe sua acessibilidade e uso. Construtores Privados (Private Constructors): Podem ser chamados apenas dentro da própria classe. Construtores privados são frequentemente usados em cenários onde você deseja impor um padrão singleton ou restringir a criação de instâncias a um método factual dentro da classe. Construtores Protegidos (Protected Constructors): Construtores protegidos são úteis quando você deseja criar uma classe base que não deve ser instanciada diretamente, mas pode ser estendida por subclasses. ```typescript class BaseClass { protected constructor() {} } class DerivedClass extends BaseClass { private value: number; constructor(value: number) { super(); this.value = value; } } // Tentar instanciar a classe base diretamente resultará em erro // const baseObj = new BaseClass(); // Erro: O construtor da classe 'BaseClass' é protegido. // Criar uma instância da classe derivada const derivedObj = new DerivedClass(10); ``` ### Modificadores de Acesso Modificadores de Acesso `private`, `protected` e `public` são usados para controlar a visibilidade e acessibilidade dos membros da classe, como propriedades e métodos, em classes TypeScript. Esses modificadores são essenciais para impor o encapsulamento e estabelecer limites para acessar e modificar o estado interno de uma classe. O modificador `private` restringe o acesso ao membro da classe apenas dentro da classe que o contém. O modificador `protected` permite o acesso ao membro da classe dentro da classe que o contém e suas classes derivadas. O modificador `public` fornece acesso irrestrito ao membro da classe, permitindo que ele seja acessado de qualquer lugar. ### Get e Set Getters e setters são métodos especiais que permitem definir comportamentos personalizados de acesso e modificação para propriedades de classe. Eles permitem encapsular o estado interno de um objeto e fornecer lógica adicional ao obter ou definir os valores das propriedades. No TypeScript, os getters e setters são definidos usando as palavras-chave `get` e `set`, respectivamente. Aqui está um exemplo: ```typescript class MyClass { private _myProperty: string; constructor(value: string) { this._myProperty = value; } get myProperty(): string { return this._myProperty; } set myProperty(value: string) { this._myProperty = value; } } ``` ### Auto-acessores em Classes O TypeScript versão 4.9 adiciona suporte para auto-acessores (auto-accessors), um recurso futuro do ECMAScript. Eles se assemelham a propriedades de classe, mas são declarados com a palavra-chave "accessor". ```typescript class Animal { accessor name: string; constructor(name: string) { this.name = name; } } ``` Os auto-acessores são transformados em acessores `get` e `set` privados, operando em uma propriedade inacessível. ```typescript class Animal { #__name: string; get name() { return this.#__name; } set name(value: string) { this.#__name = name; } constructor(name: string) { this.name = name; } } ``` ### this No TypeScript, a palavra-chave `this` refere-se à instância atual de uma classe dentro de seus métodos ou construtores. Ela permite acessar e modificar as propriedades e métodos da classe de dentro de seu próprio escopo. Fornece uma maneira de acessar e manipular o estado interno de um objeto dentro de seus próprios métodos. ```typescript class Person { private name: string; constructor(name: string) { this.name = name; } public introduce(): void { console.log(`Olá, meu nome é ${this.name}.`); } } const person1 = new Person('Alice'); person1.introduce(); // Olá, meu nome é Alice. ``` ### Propriedades de Parâmetro As propriedades de parâmetro permitem declarar e inicializar propriedades de classe diretamente dentro dos parâmetros do construtor, evitando o código repetitivo (boilerplate). Exemplo: ```typescript class Person { constructor( private name: string, public age: number ) { // As palavras-chave "private" e "public" no construtor // declaram e inicializam automaticamente as propriedades de classe correspondentes. } public introduce(): void { console.log( `Olá, meu nome é ${this.name} e eu tenho ${this.age} anos.` ); } } const person = new Person('Alice', 25); person.introduce(); ``` ### Classes Abstratas Classes Abstratas são usadas no TypeScript principalmente para herança; elas fornecem uma maneira de definir propriedades e métodos comuns que podem ser herdados por subclasses. Isso é útil quando você deseja definir um comportamento comum e garantir que as subclasses implementem certos métodos. Elas fornecem uma maneira de criar uma hierarquia de classes onde a classe base abstrata fornece uma interface compartilhada e funcionalidade comum para as subclasses. ```typescript abstract class Animal { protected name: string; constructor(name: string) { this.name = name; } abstract makeSound(): void; } class Cat extends Animal { makeSound(): void { console.log(`${this.name} mia (meows).`); } } const cat = new Cat('Whiskers'); cat.makeSound(); // Saída: Whiskers mia (meows). ``` ### Com Genéricos Classes com genéricos permitem definir classes reutilizáveis que podem trabalhar com diferentes tipos. ```typescript class Container { private item: T; constructor(item: T) { this.item = item; } getItem(): T { return this.item; } setItem(item: T): void { this.item = item; } } const container1 = new Container(42); console.log(container1.getItem()); // 42 const container2 = new Container('Olá'); container2.setItem('Mundo'); console.log(container2.getItem()); // Mundo ``` ### Decoradores (Decorators) Os decoradores fornecem um mecanismo para adicionar metadados, modificar comportamentos, validar ou estender a funcionalidade do elemento alvo. São funções que são executadas em tempo de execução. Múltiplos decoradores podem ser aplicados a uma declaração. Os decoradores são recursos experimentais, e os exemplos a seguir são compatíveis apenas com a versão 5 do TypeScript ou superior usando ES6. Para versões do TypeScript anteriores à 5, eles devem ser habilitados usando a propriedade `experimentalDecorators` em seu `tsconfig.json` ou usando `--experimentalDecorators` na sua linha de comando (mas o exemplo a seguir não funcionará). Alguns dos casos de uso comuns para decoradores incluem: * Observar mudanças de propriedade. * Observar chamadas de métodos. * Adicionar propriedades ou métodos extras. * Validação em tempo de execução. * Serialização e desserialização automática. * Registro (Logging). * Autorização e autenticação. * Proteção contra erros (Error guarding). Nota: Decoradores para a versão 5 não permitem decorar parâmetros. Tipos de decoradores: #### Decoradores de Classe (Class Decorators) Os Decoradores de Classe são úteis para estender uma classe existente, como adicionar propriedades ou métodos, ou coletar instâncias de uma classe. No exemplo a seguir, adicionamos um método `toString` que converte a classe em uma representação de string. ```typescript type Constructor = new (...args: any[]) => T; function toString( Value: Class, context: ClassDecoratorContext ) { return class extends Value { constructor(...args: any[]) { super(...args); console.log(JSON.stringify(this)); console.log(JSON.stringify(context)); } }; } @toString class Person { name: string; constructor(name: string) { this.name = name; } greet() { return 'Hello, ' + this.name; } } const person = new Person('Simon'); /* Registra: {"name":"Simon"} {"kind":"class","name":"Person"} */ ``` #### Decorador de Propriedade (Property Decorator) Os decoradores de propriedade são úteis para modificar o comportamento de uma propriedade, como alterar os valores de inicialização. No código a seguir, temos um script que define uma propriedade para estar sempre em letras maiúsculas: ```typescript function upperCase( target: undefined, context: ClassFieldDecoratorContext ) { return function (this: T, value: string) { return value.toUpperCase(); }; } class MyClass { @upperCase prop1 = 'hello!'; } console.log(new MyClass().prop1); // Registra: HELLO! ``` #### Decorador de Método (Method Decorator) Os decoradores de método permitem alterar ou aprimorar o comportamento dos métodos. Abaixo está um exemplo de um registrador (logger) simples: ```typescript function log( target: (this: This, ...args: Args) => Return, context: ClassMethodDecoratorContext< This, (this: This, ...args: Args) => Return > ) { const methodName = String(context.name); function replacementMethod(this: This, ...args: Args): Return { console.log(`LOG: Entrando no método '${methodName}'.`); const result = target.call(this, ...args); console.log(`LOG: Saindo do método '${methodName}'.`); return result; } return replacementMethod; } class MyClass { @log sayHello() { console.log('Hello!'); } } new MyClass().sayHello(); ``` Isso registra: ```shell LOG: Entrando no método 'sayHello'. Hello! LOG: Saindo do método 'sayHello'. ``` #### Decoradores de Getter e Setter Decoradores de getter e setter permitem alterar ou aprimorar o comportamento dos acessores de classe. Eles são úteis, por exemplo, para validar atribuições de propriedades. Aqui está um exemplo simples de um decorador de getter: ```typescript function range(min: number, max: number) { return function ( target: (this: This) => Return, context: ClassGetterDecoratorContext ) { return function (this: This): Return { const value = target.call(this); if (value < min || value > max) { throw 'Invalid'; } Object.defineProperty(this, context.name, { value, enumerable: true, }); return value; }; }; } class MyClass { private _value = 0; constructor(value: number) { this._value = value; } @range(1, 100) get getValue(): number { return this._value; } } const obj = new MyClass(10); console.log(obj.getValue); // Válido: 10 const obj2 = new MyClass(999); console.log(obj2.getValue); // Lança: Invalid! ``` #### Metadados de Decorador (Decorator Metadata) Os Metadados de Decorador simplificam o processo para que os decoradores apliquem e utilizem metadados em qualquer classe. Eles podem acessar uma nova propriedade de metadados no objeto de contexto, que pode servir como uma chave para primitivos e objetos. As informações de metadados podem ser acessadas na classe via `Symbol.metadata`. Os metadados podem ser usados para vários fins, como depuração, serialização ou injeção de dependência com decoradores. ```typescript //@ts-ignore Symbol.metadata ??= Symbol('Symbol.metadata'); // Polify simples type Context = | ClassFieldDecoratorContext | ClassAccessorDecoratorContext | ClassMethodDecoratorContext; // O contexto contém os metadados da propriedade: DecoratorMetadata function setMetadata(_target: any, context: Context) { // Define o objeto de metadados com um valor primitivo context.metadata[context.name] = true; } class MyClass { @setMetadata a = 123; @setMetadata accessor b = 'b'; @setMetadata fn() {} } const metadata = MyClass[Symbol.metadata]; // Obtém as informações de metadados console.log(JSON.stringify(metadata)); // {"bar":true,"baz":true,"foo":true} ``` ### Herança Herança refere-se ao mecanismo pelo qual uma classe pode herdar propriedades e métodos de outra classe, conhecida como classe base ou superclasse. A classe derivada, também chamada de classe filha ou subclasse, pode estender e especializar a funcionalidade da classe base adicionando novas propriedades e métodos ou substituindo (overriding) os existentes. ```typescript class Animal { name: string; constructor(name: string) { this.name = name; } speak(): void { console.log('O animal faz um som'); } } class Dog extends Animal { breed: string; constructor(name: string, breed: string) { super(name); this.breed = breed; } speak(): void { console.log('Woof! Woof!'); } } // Cria uma instância da classe base const animal = new Animal('Animal Genérico'); animal.speak(); // O animal faz um som // Cria uma instância da classe derivada const dog = new Dog('Max', 'Labrador'); dog.speak(); // Woof! Woof!" ``` O TypeScript não suporta herança múltipla no sentido tradicional e, em vez disso, permite a herança de uma única classe base. O TypeScript suporta múltiplas interfaces. Uma interface pode definir um contrato para a estrutura de um objeto, e uma classe pode implementar múltiplas interfaces. Isso permite que uma classe herde comportamento e estrutura de múltiplas fontes. ```typescript interface Flyable { fly(): void; } interface Swimmable { swim(): void; } class FlyingFish implements Flyable, Swimmable { fly() { console.log('Voando...'); } swim() { console.log('Nadando...'); } } const flyingFish = new FlyingFish(); flyingFish.fly(); flyingFish.swim(); ``` A palavra-chave `class` no TypeScript, assim como no JavaScript, é frequentemente chamada de açúcar sintático (syntactic sugar). Ela foi introduzida no ECMAScript 2015 (ES6) para oferecer uma sintaxe mais familiar para criar e trabalhar com objetos de maneira baseada em classes. No entanto, é importante notar que o TypeScript, sendo um superconjunto do JavaScript, acaba sendo compilado para JavaScript, que permanece baseado em protótipos em seu núcleo. ### Estáticos (Statics) O TypeScript possui membros estáticos. Para acessar os membros estáticos de uma classe, você pode usar o nome da classe seguido por um ponto, sem a necessidade de criar um objeto. ```typescript class OfficeWorker { static memberCount: number = 0; constructor(private name: string) { OfficeWorker.memberCount++; } } const w1 = new OfficeWorker('James'); const w2 = new OfficeWorker('Simon'); const total = OfficeWorker.memberCount; console.log(total); // 2 ``` ### Inicialização de Propriedade Existem várias maneiras de inicializar propriedades de uma classe no TypeScript: Em linha (Inline): No exemplo a seguir, esses valores iniciais serão usados quando uma instância da classe for criada. ```typescript class MyClass { property1: string = 'valor padrão'; property2: number = 42; } ``` No construtor: ```typescript class MyClass { property1: string; property2: number; constructor() { this.property1 = 'valor padrão'; this.property2 = 42; } } ``` Usando parâmetros do construtor: ```typescript class MyClass { constructor( private property1: string = 'valor padrão', public property2: number = 42 ) { // Não há necessidade de atribuir os valores às propriedades explicitamente. } log() { console.log(this.property2); } } const x = new MyClass(); x.log(); ``` ### Sobrecarga de Método A sobrecarga de método permite que uma classe tenha múltiplos métodos com o mesmo nome, mas diferentes tipos de parâmetros ou um número diferente de parâmetros. Isso nos permite chamar um método de diferentes maneiras com base nos argumentos passados. ```typescript class MyClass { add(a: number, b: number): number; // Assinatura de sobrecarga 1 add(a: string, b: string): string; // Assinatura de sobrecarga 2 add(a: number | string, b: number | string): number | string { if (typeof a === 'number' && typeof b === 'number') { return a + b; } if (typeof a === 'string' && typeof b === 'string') { return a.concat(b); } throw new Error('Argumentos inválidos'); } } const r = new MyClass(); console.log(r.add(10, 5)); // Registra 15 ``` ================================================ FILE: website/src/content/docs/pt-br/book/common-built-in-js-objects.md ================================================ --- title: Objetos JS Integrados Comuns sidebar: order: 50 label: 50. Objetos JS Integrados Comuns --- O TypeScript é um superconjunto do JavaScript e inclui todos os objetos integrados do JavaScript comumente usados. Você pode encontrar uma lista extensa desses objetos no site de documentação da Mozilla Developer Network (MDN): [https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Global_Objects](https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Global_Objects) Aqui está uma lista de alguns objetos integrados do JavaScript comumente usados: * Function * Object * Boolean * Error * Number * BigInt * Math * Date * String * RegExp * Array * Map * Set * Promise * Intl ================================================ FILE: website/src/content/docs/pt-br/book/conditional-types.md ================================================ --- title: Tipos Condicionais (Conditional Types) sidebar: order: 39 label: 39. Tipos Condicionais (Conditional Types) --- Tipos Condicionais são uma forma de criar um tipo que depende de uma condição, onde o tipo a ser criado é determinado com base no resultado da condição. Eles são definidos usando a palavra-chave `extends` e um operador ternário para escolher condicionalmente entre dois tipos. ```typescript type IsArray = T extends any[] ? true : false; const myArray = [1, 2, 3]; const myNumber = 42; type IsMyArrayAnArray = IsArray; // Tipo true type IsMyNumberAnArray = IsArray; // Tipo false ``` ================================================ FILE: website/src/content/docs/pt-br/book/control-flow-analysis.md ================================================ --- title: Análise de Fluxo de Controle sidebar: order: 22 label: 22. Análise de Fluxo de Controle --- A Análise de Fluxo de Controle (Control Flow Analysis) no TypeScript é uma forma de analisar estaticamente o fluxo do código para inferir os tipos das variáveis, permitindo que o compilador estreite os tipos dessas variáveis conforme necessário, com base nos resultados da análise. Antes do TypeScript 4.4, a análise de fluxo de código só seria aplicada ao código dentro de uma instrução `if`, mas a partir do TypeScript 4.4, ela também pode ser aplicada a expressões condicionais e acessos a propriedades discriminantes referenciados indiretamente por meio de variáveis `const`. Por exemplo: ```typescript const f1 = (x: unknown) => { const isString = typeof x === 'string'; if (isString) { x.length; } }; const f2 = ( obj: { kind: 'foo'; foo: string } | { kind: 'bar'; bar: number } ) => { const isFoo = obj.kind === 'foo'; if (isFoo) { obj.foo; } else { obj.bar; } }; ``` Alguns exemplos onde o estreitamento não ocorre: ```typescript const f1 = (x: unknown) => { let isString = typeof x === 'string'; if (isString) { x.length; // Erro, sem estreitamento porque isString não é const } }; const f6 = ( obj: { kind: 'foo'; foo: string } | { kind: 'bar'; bar: number } ) => { const isFoo = obj.kind === 'foo'; obj = obj; if (isFoo) { obj.foo; // Erro, sem estreitamento porque obj é atribuído no corpo da função } }; ``` Notas: Até cinco níveis de indireção são analisados em expressões condicionais. ================================================ FILE: website/src/content/docs/pt-br/book/differences-between-type-and-interface.md ================================================ --- title: Diferenças entre Type e Interface sidebar: order: 53 label: 53. Diferenças entre Type e Interface --- Mesclagem de declarações (aumento): As interfaces suportam a mesclagem de declarações, o que significa que você pode definir várias interfaces com o mesmo nome, e o TypeScript as mesclará em uma única interface com as propriedades e métodos combinados. Por outro lado, os tipos (types) não suportam a mesclagem de declarações. Isso pode ser útil quando você deseja adicionar funcionalidades extras ou personalizar tipos existentes sem modificar as definições originais ou corrigir tipos ausentes ou incorretos. ```typescript interface A { x: string; } interface A { y: string; } const j: A = { x: 'xx', y: 'yy', }; ``` Estendendo outros tipos/interfaces: Tanto tipos quanto interfaces podem estender outros tipos/interfaces, mas a sintaxe é diferente. Com as interfaces, você usa a palavra-chave `extends` para herdar propriedades e métodos de outras interfaces. No entanto, uma interface não pode estender um tipo complexo, como um tipo união. ```typescript interface A { x: string; y: number; } interface B extends A { z: string; } const car: B = { x: 'x', y: 123, z: 'z', }; ``` Para tipos, você usa o operador `&` para combinar múltiplos tipos em um único tipo (interseção). ```typescript interface A { x: string; y: number; } type B = A & { j: string; }; const c: B = { x: 'x', y: 123, j: 'j', }; ``` Tipos União e Interseção: Os tipos (types) são mais flexíveis quando se trata de definir Tipos União e Interseção. Com a palavra-chave `type`, você pode criar facilmente tipos união usando o operador `|` e tipos interseção usando o operador `&`. Embora as interfaces também possam representar tipos união indiretamente, elas não possuem suporte integrado para tipos interseção. ```typescript type Department = 'dep-x' | 'dep-y'; // União type Person = { name: string; age: number; }; type Employee = { id: number; department: Department; }; type EmployeeInfo = Person & Employee; // Interseção ``` Exemplo com interfaces: ```typescript interface A { x: 'x'; } interface B { y: 'y'; } type C = A | B; // União de interfaces ``` ================================================ FILE: website/src/content/docs/pt-br/book/discriminated-unions.md ================================================ --- title: Uniões Discriminadas sidebar: order: 24 label: 24. Uniões Discriminadas --- Uniões Discriminadas (Discriminated Unions) no TypeScript são um tipo de união que usa uma propriedade comum, conhecida como discriminante, para estreitar o conjunto de tipos possíveis para a união. ```typescript type Square = { kind: 'square'; // Discriminante size: number; }; type Circle = { kind: 'circle'; // Discriminante radius: number; }; type Shape = Square | Circle; const area = (shape: Shape) => { switch (shape.kind) { case 'square': return Math.pow(shape.size, 2); case 'circle': return Math.PI * Math.pow(shape.radius, 2); } }; const square: Square = { kind: 'square', size: 5 }; const circle: Circle = { kind: 'circle', radius: 2 }; console.log(area(square)); // 25 console.log(area(circle)); // 12.566370614359172 ``` ================================================ FILE: website/src/content/docs/pt-br/book/distributive-conditional-types.md ================================================ --- title: Tipos Condicionais Distributivos sidebar: order: 40 label: 40. Tipos Condicionais Distributivos --- Tipos Condicionais Distributivos são um recurso que permite que um tipo seja distribuído sobre uma união de tipos, aplicando uma transformação a cada membro da união individualmente. Isso pode ser especialmente útil ao trabalhar com tipos mapeados ou tipos de ordem superior. ```typescript type Nullable = T extends any ? T | null : never; type NumberOrBool = number | boolean; type NullableNumberOrBool = Nullable; // number | boolean | null ``` ================================================ FILE: website/src/content/docs/pt-br/book/downloads-and-website.md ================================================ --- title: Downloads e site sidebar: order: 3 label: 3. Downloads e site --- Você também pode baixar a versão Epub: [https://github.com/gibbok/typescript-book/tree/main/downloads](https://github.com/gibbok/typescript-book/tree/main/downloads) Uma versão online está disponível em: [https://gibbok.github.io/typescript-book](https://gibbok.github.io/typescript-book) ================================================ FILE: website/src/content/docs/pt-br/book/enums.md ================================================ --- title: Enums sidebar: order: 19 label: 19. Enums --- No TypeScript, um `enum` é um conjunto de valores constantes nomeados. ```typescript enum Color { Red = '#ff0000', Green = '#00ff00', Blue = '#0000ff', } ``` Enums podem ser definidos de diferentes maneiras: ### Enums numéricos No TypeScript, um Enum Numérico é um Enum onde cada constante recebe um valor numérico, começando em 0 por padrão. ```typescript enum Size { Small, // o valor começa de 0 Medium, Large, } ``` É possível especificar valores personalizados atribuindo-os explicitamente: ```typescript enum Size { Small = 10, Medium, Large, } console.log(Size.Medium); // 11 ``` ### Enums de string No TypeScript, um Enum de String é um Enum onde cada constante recebe um valor de string. ```typescript enum Language { English = 'EN', Spanish = 'ES', } ``` Nota: O TypeScript permite o uso de Enums heterogêneos, onde membros de string e numéricos podem coexistir. ### Enums constantes Um enum constante (const enum) no TypeScript é um tipo especial de Enum onde todos os valores são conhecidos em tempo de compilação e são inseridos diretamente (inlined) onde quer que o enum seja usado, resultando em um código mais eficiente. ```typescript const enum Language { English = 'EN', Spanish = 'ES', } console.log(Language.English); ``` Será compilado para: ```typescript console.log('EN' /* Language.English */); ``` Notas: Enums Constantes têm valores fixos (hardcoded), apagando o Enum, o que pode ser mais eficiente em bibliotecas autocontidas, mas geralmente não é desejável. Além disso, enums constantes não podem ter membros computados. ### Mapeamento reverso No TypeScript, os mapeamentos reversos em Enums referem-se à capacidade de recuperar o nome do membro do Enum a partir de seu valor. Por padrão, os membros do Enum têm mapeamentos diretos (forward mappings) do nome para o valor, mas mapeamentos reversos podem ser criados definindo explicitamente os valores para cada membro. Os mapeamentos reversos são úteis quando você precisa procurar um membro do Enum pelo seu valor ou quando precisa iterar sobre todos os membros do Enum. Note que apenas membros de enums numéricos gerarão mapeamentos reversos, enquanto membros de Enums de String não possuem um mapeamento reverso gerado. O seguinte enum: ```typescript enum Grade { A = 90, B = 80, C = 70, F = 'fail', } ``` Compila para: ```javascript 'use strict'; var Grade; (function (Grade) { Grade[(Grade['A'] = 90)] = 'A'; Grade[(Grade['B'] = 80)] = 'B'; Grade[(Grade['C'] = 70)] = 'C'; Grade['F'] = 'fail'; })(Grade || (Grade = {})); ``` Portanto, mapear valores para chaves funciona para membros de enums numéricos, mas não para membros de enums de string: ```typescript enum Grade { A = 90, B = 80, C = 70, F = 'fail', } const myGrade = Grade.A; console.log(Grade[myGrade]); // A console.log(Grade[90]); // A const failGrade = Grade.F; console.log(failGrade); // fail console.log(Grade[failGrade]); // Element implicitly has an 'any' type because index expression is not of type 'number'. ``` ### Enums de ambiente Um enum de ambiente no TypeScript é um tipo de Enum que é definido em um arquivo de declaração (*.d.ts) sem uma implementação associada. Ele permite definir um conjunto de constantes nomeadas que podem ser usadas de forma segura em relação aos tipos em diferentes arquivos, sem ter que importar os detalhes da implementação em cada arquivo. ### Membros computados e constantes No TypeScript, um membro computado é um membro de um Enum que possui um valor calculado em tempo de execução, enquanto um membro constante é um membro cujo valor é definido em tempo de compilação e não pode ser alterado durante o tempo de execução. Membros computados são permitidos em Enums regulares, enquanto membros constantes são permitidos tanto em enums regulares quanto em enums constantes (const enums). ```typescript // Membros constantes enum Color { Red = 1, Green = 5, Blue = Red + Green, } console.log(Color.Blue); // geração 6 em tempo de compilação ``` ```typescript // Membros computados enum Color { Red = 1, Green = Math.pow(2, 2), Blue = Math.floor(Math.random() * 3) + 1, } console.log(Color.Blue); // número aleatório gerado em tempo de execução ``` Enums são representados por uniões compostas pelos tipos de seus membros. Os valores de cada membro podem ser determinados por meio de expressões constantes ou não constantes, com membros que possuem valores constantes recebendo tipos literais. Para ilustrar, considere a declaração do tipo E e seus subtipos E.A, E.B e E.C. Neste caso, E representa a união E.A | E.B | E.C. ```typescript const identity = (value: number) => value; enum E { A = 2 * 5, // Literal numérico B = 'bar', // Literal de string C = identity(42), // Computado opaco } console.log(E.C); //42 ``` ================================================ FILE: website/src/content/docs/pt-br/book/erased-structural-types.md ================================================ --- title: Tipos Estruturais Apagados sidebar: order: 56 label: 56. Tipos Estruturais Apagados --- No TypeScript, os objetos não precisam corresponder a um tipo exato e específico. Por exemplo, se criarmos um objeto que satisfaça os requisitos de uma interface, podemos utilizar esse objeto em locais onde essa interface é necessária, mesmo que não haja uma conexão explícita entre eles. Exemplo: ```typescript type NameProp1 = { prop1: string; }; function log(x: NameProp1) { console.log(x.prop1); } const obj = { prop2: 123, prop1: 'Origin', }; log(obj); // Válido ``` ================================================ FILE: website/src/content/docs/pt-br/book/exhaustiveness-checking.md ================================================ --- title: Verificação de exaustividade sidebar: order: 26 label: 26. Verificação de exaustividade --- A verificação de exaustividade (exhaustiveness checking) é um recurso no TypeScript que garante que todos os casos possíveis de uma união discriminada sejam tratados em uma instrução `switch` ou `if`. ```typescript type Direction = 'up' | 'down'; const move = (direction: Direction) => { switch (direction) { case 'up': console.log('Movendo para cima'); break; case 'down': console.log('Movendo para baixo'); break; default: const exhaustiveCheck: never = direction; console.log(exhaustiveCheck); // Esta linha nunca será executada } }; ``` O tipo `never` é usado para garantir que o caso `default` seja exaustivo e que o TypeScript aponte um erro se um novo valor for adicionado ao tipo `Direction` sem ser tratado na instrução switch. ================================================ FILE: website/src/content/docs/pt-br/book/exploring-the-type-system.md ================================================ --- title: Explorando o Sistema de Tipos sidebar: order: 9 label: 9. Explorando o Sistema de Tipos --- ### O Serviço de Linguagem do TypeScript O Serviço de Linguagem do TypeScript, também conhecido como tsserver, oferece vários recursos, como relatório de erros, diagnósticos, compilar ao salvar, renomeação, ir para definição, listas de preenchimento, ajuda de assinatura e muito mais. É usado principalmente por ambientes de desenvolvimento integrados (IDEs) para fornecer suporte ao IntelliSense. Ele se integra perfeitamente ao Visual Studio Code e é utilizado por ferramentas como Conquer of Completion (Coc). Os desenvolvedores podem aproveitar uma API dedicada e criar seus próprios plugins de serviço de linguagem personalizados para aprimorar a experiência de edição do TypeScript. Isso pode ser particularmente útil para implementar recursos especiais de linting ou habilitar o preenchimento automático para uma linguagem de modelagem personalizada. Um exemplo de plugin personalizado do mundo real é o "typescript-styled-plugin", que fornece relatórios de erros de sintaxe e suporte IntelliSense para propriedades CSS em componentes estilizados (styled components). Para mais informações e guias de início rápido, você pode consultar o Wiki oficial do TypeScript no GitHub: [https://github.com/microsoft/TypeScript/wiki/](https://github.com/microsoft/TypeScript/wiki/) ### Tipagem Estrutural O TypeScript é baseado em um sistema de tipos estrutural. Isso significa que a compatibilidade e a equivalência de tipos são determinadas pela estrutura ou definição real do tipo, em vez de seu nome ou local de declaração, como em sistemas de tipos nominativos como C# ou C++. O sistema de tipos estrutural do TypeScript foi projetado com base em como o sistema de tipagem dinâmica "duck typing" do JavaScript funciona durante o tempo de execução. O exemplo a seguir é um código TypeScript válido. Como você pode observar, "X" e "Y" têm o mesmo membro "a", embora tenham nomes de declaração diferentes. Os tipos são determinados por suas estruturas e, neste caso, como as estruturas são as mesmas, eles são compatíveis e válidos. ```typescript type X = { a: string; }; type Y = { a: string; }; const x: X = { a: 'a' }; const y: Y = x; // Válido ``` ### Regras Fundamentais de Comparação do TypeScript O processo de comparação do TypeScript é recursivo e executado em tipos aninhados em qualquer nível. Um tipo "X" é compatível com "Y" se "Y" tiver pelo menos os mesmos membros que "X". ```typescript type X = { a: string; }; const y = { a: 'A', b: 'B' }; // Válido, pois tem pelo menos os mesmos membros que X const r: X = y; ``` Os parâmetros da função são comparados por tipos, não por seus nomes: ```typescript type X = (a: number) => void; type Y = (a: number) => void; let x: X = (j: number) => undefined; let y: Y = (k: number) => undefined; y = x; // Válido x = y; // Válido ``` Os tipos de retorno da função devem ser os mesmos: ```typescript type X = (a: number) => undefined; type Y = (a: number) => number; let x: X = (a: number) => undefined; let y: Y = (a: number) => 1; y = x; // Inválido x = y; // Inválido ``` O tipo de retorno de uma função de origem deve ser um subtipo do tipo de retorno de uma função de destino: ```typescript let x = () => ({ a: 'A' }); let y = () => ({ a: 'A', b: 'B' }); x = y; // Válido y = x; // Inválido, o membro b está faltando ``` Descartar parâmetros de função é permitido, pois é uma prática comum no JavaScript, por exemplo, usando "Array.prototype.map()": ```typescript [1, 2, 3].map((element, _index, _array) => element + 'x'); ``` Portanto, as seguintes declarações de tipo são completamente válidas: ```typescript type X = (a: number) => undefined; type Y = (a: number, b: number) => undefined; let x: X = (a: number) => undefined; let y: Y = (a: number) => undefined; // Falta o parâmetro b y = x; // Válido ``` Quaisquer parâmetros opcionais adicionais do tipo de origem são válidos: ```typescript type X = (a: number, b?: number, c?: number) => undefined; type Y = (a: number) => undefined; let x: X = a => undefined; let y: Y = a => undefined; y = x; // Válido x = y; // Válido ``` Quaisquer parâmetros opcionais do tipo de destino sem parâmetros correspondentes no tipo de origem são válidos e não constituem um erro: ```typescript type X = (a: number) => undefined; type Y = (a: number, b?: number) => undefined; let x: X = a => undefined; let y: Y = a => undefined; y = x; // Válido x = y; // Válido ``` O parâmetro rest é tratado como uma série infinita de parâmetros opcionais: ```typescript type X = (a: number, ...rest: number[]) => undefined; let x: X = a => undefined; // Válido ``` Funções com sobrecargas são válidas se a assinatura da sobrecarga for compatível com sua assinatura de implementação: ```typescript function x(a: string): void; function x(a: string, b: number): void; function x(a: string, b?: number): void { console.log(a, b); } x('a'); // Válido x('a', 1); // Válido function y(a: string): void; // Inválido, não compatível com a assinatura de implementação function y(a: string, b: number): void; function y(a: string, b: number): void { console.log(a, b); } y('a'); y('a', 1); ``` A comparação de parâmetros de função é bem-sucedida se os parâmetros de origem e de destino forem atribuíveis a supertipos ou subtipos (bivariância). ```typescript // Supertipo class X { a: string; constructor(value: string) { this.a = value; } } // Subtipo class Y extends X {} // Subtipo class Z extends X {} type GetA = (x: X) => string; const getA: GetA = x => x.a; // A bivariância aceita supertipos console.log(getA(new X('x'))); // Válido console.log(getA(new Y('Y'))); // Válido console.log(getA(new Z('z'))); // Válido ``` Enums são comparáveis e válidos com números e vice-versa, mas comparar valores de Enum de diferentes tipos de Enum é inválido. ```typescript enum X { A, B, } enum Y { A, B, C, } const xa: number = X.A; // Válido const ya: Y = 0; // Válido X.A === Y.A; // Inválido ``` Instâncias de uma classe estão sujeitas a uma verificação de compatibilidade para seus membros privados e protegidos: ```typescript class X { public a: string; constructor(value: string) { this.a = value; } } class Y { private a: string; constructor(value: string) { this.a = value; } } let x: X = new Y('y'); // Inválido ``` A verificação de comparação não leva em consideração as diferentes hierarquias de herança, por exemplo: ```typescript class X { public a: string; constructor(value: string) { this.a = value; } } class Y extends X { public a: string; constructor(value: string) { super(value); this.a = value; } } class Z { public a: string; constructor(value: string) { this.a = value; } } let x: X = new X('x'); let y: Y = new Y('y'); let z: Z = new Z('z'); x === y; // Válido x === z; // Válido mesmo que z seja de uma hierarquia de herança diferente ``` Genéricos são comparados usando suas estruturas baseadas no tipo resultante após a aplicação do parâmetro genérico; apenas o resultado final é comparado como um tipo não genérico. ```typescript interface X { a: T; } let x: X = { a: 1 }; let y: X = { a: 'a' }; x === y; // Inválido, pois o argumento de tipo é usado na estrutura final ``` ```typescript interface X {} const x: X = 1; const y: X = 'a'; x === y; // Válido, pois o argumento de tipo não é usado na estrutura final ``` Quando os genéricos não têm seu argumento de tipo especificado, todos os argumentos não especificados são tratados como tipos com "any": ```typescript type X = (x: T) => T; type Y = (y: K) => K; let x: X = x => x; let y: Y = y => y; x = y; // Válido ``` Lembre-se: ```typescript let a: number = 1; let b: number = 2; a = b; // Válido, tudo é atribuível a si mesmo let c: any; c = 1; // Válido, todos os tipos são atribuíveis a any let d: unknown; d = 1; // Válido, todos os tipos são atribuíveis a unknown let e: unknown; let e1: unknown = e; // Válido, unknown só é atribuível a si mesmo e a any let e2: any = e; // Válido let e3: number = e; // Inválido let f: never; f = 1; // Inválido, nada é atribuível a never let g: void; let g1: any; g = 1; // Inválido, void não é atribuível a nada, exceto any, nem nada é atribuível a ele g = g1; // Válido ``` Observe que quando "strictNullChecks" está habilitado, "null" e "undefined" são tratados de forma semelhante a "void"; caso contrário, são semelhantes a "never". ### Tipos como Conjuntos No TypeScript, um tipo é um conjunto de valores possíveis. Este conjunto também é conhecido como o domínio do tipo. Cada valor de um tipo pode ser visto como um elemento em um conjunto. Um tipo estabelece as restrições que cada elemento no conjunto deve satisfazer para ser considerado um membro desse conjunto. A principal tarefa do TypeScript é verificar se um conjunto é um subconjunto de outro. O TypeScript suporta vários tipos de conjuntos: | Termo do conjunto | TypeScript | Notas | | ---------------------- | ------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | | Conjunto vazio | never | "never" não contém nada além de si mesmo | | Conjunto de elemento único | undefined / null / tipo literal | | | Conjunto finito | boolean / união | | | Conjunto infinito | string / number / objeto | | | Conjunto universal | any / unknown | Cada elemento é um membro de "any" e cada conjunto é um subconjunto dele / "unknown" é uma contraparte segura em termos de tipo do "any" | Aqui estão alguns exemplos: | TypeScript | Termo do conjunto | Exemplo | | ----------------------- | -------------------------- | ------------------------------------------------------------------------------------- | | never | ∅ (conjunto vazio) | const x: never = 'x'; // Erro: O tipo 'string' não pode ser atribuído ao tipo 'never' | | | | | Tipo literal | Conjunto de elemento único | type X = 'X'; | | | | type Y = 7; | | | | | Valor atribuível a T | Valor ∈ T (membro de) | type XY = 'X' \| 'Y'; | | | | const x: XY = 'X'; | | | | | T1 atribuível a T2 | T1 ⊆ T2 (subconjunto de) | type XY = 'X' \| 'Y'; | | | | const x: XY = 'X'; | | | | const j: XY = 'J'; // O tipo '"J"' não pode ser atribuído ao tipo 'XY'. | | | | | | T1 extends T2 | T1 ⊆ T2 (subconjunto de) | type X = 'X' extends string ? true : false; | | | | | T1 \| T2 | T1 ∪ T2 (união) | type XY = 'X' \| 'Y'; | | | | type JK = 1 \| 2; | | | | | T1 & T2 | T1 ∩ T2 (interseção) | type X = \{ a: string \} | | | | type Y = \{ b: string \} | | | | type XY = X & Y | | | | const x: XY = \{ a: 'a', b: 'b' \} | | | | | unknown | Conjunto universal | const x: unknown = 1 | Uma união, (T1 | T2), cria um conjunto mais amplo (ambos): ```typescript type X = { a: string; }; type Y = { b: string; }; type XY = X | Y; const r: XY = { a: 'a', b: 'x' }; // Válido ``` Uma interseção, (T1 & T2), cria um conjunto mais estreito (apenas o que é compartilhado): ```typescript type X = { a: string; }; type Y = { a: string; b: string; }; type XY = X & Y; const r: XY = { a: 'a' }; // Inválido const j: XY = { a: 'a', b: 'b' }; // Válido ``` A palavra-chave `extends` pode ser considerada como "subconjunto de" neste contexto. Ela define uma restrição para um tipo. O `extends` usado com um genérico trata o genérico como um conjunto infinito e o restringe a um tipo mais específico. Observe que o `extends` nada tem a ver com hierarquia no sentido de Orientação a Objetos (não existe esse conceito no TypeScript). O TypeScript trabalha com conjuntos e não possui uma hierarquia estrita; de fato, como no exemplo abaixo, dois tipos podem se sobrepor sem que nenhum seja um subtipo do outro (o TypeScript considera a estrutura, a forma dos objetos). ```typescript interface X { a: string; } interface Y extends X { b: string; } interface Z extends Y { c: string; } const z: Z = { a: 'a', b: 'b', c: 'c' }; interface X1 { a: string; } interface Y1 { a: string; b: string; } interface Z1 { a: string; b: string; c: string; } const z1: Z1 = { a: 'a', b: 'b', c: 'c' }; const r: Z1 = z; // Válido ``` ### Atribuir um tipo: Declarações de Tipo e Asserções de Tipo Um tipo pode ser atribuído de diferentes maneiras no TypeScript: #### Declaração de Tipo No exemplo a seguir, usamos x: X (": Tipo") para declarar um tipo para a variável x. ```typescript type X = { a: string; }; // Declaração de tipo const x: X = { a: 'a', }; ``` Se a variável não estiver no formato especificado, o TypeScript relatará um erro. Por exemplo: ```typescript type X = { a: string; }; const x: X = { a: 'a', b: 'b', // Erro: O objeto literal só pode especificar propriedades conhecidas }; ``` #### Asserção de Tipo É possível adicionar uma asserção usando a palavra-chave `as`. Isso informa ao compilador que o desenvolvedor tem mais informações sobre um tipo e silencia quaisquer erros que possam ocorrer. Por exemplo: ```typescript type X = { a: string; }; const x = { a: 'a', b: 'b', } as X; ``` No exemplo acima, o objeto x é asseverado como tendo o tipo X usando a palavra-chave `as`. Isso informa ao compilador TypeScript que o objeto está em conformidade com o tipo especificado, embora tenha uma propriedade b adicional não presente na definição do tipo. Asserções de tipo são úteis em situações onde um tipo mais específico precisa ser especificado, especialmente ao trabalhar com o DOM. Por exemplo: ```typescript const myInput = document.getElementById('my_input') as HTMLInputElement; ``` Aqui, a asserção de tipo `as HTMLInputElement` é usada para dizer ao TypeScript que o resultado de `getElementById` deve ser tratado como um `HTMLInputElement`. Asserções de tipo também podem ser usadas para mapear chaves novamente, conforme mostrado no exemplo abaixo com literais de template: ```typescript type J = { [Property in keyof Type as `prefix_${string & Property}`]: () => Type[Property]; }; type X = { a: string; b: number; }; type Y = J; ``` Neste exemplo, o tipo `J` usa um tipo mapeado com um literal de template para mapear as chaves de `Type`. Ele cria novas propriedades com um "prefix_" adicionado a cada chave, e seus valores correspondentes são funções que retornam os valores originais da propriedade. Vale a pena notar que, ao usar uma asserção de tipo, o TypeScript não executará a verificação de excesso de propriedades. Portanto, geralmente é preferível usar uma Declaração de Tipo quando a estrutura do objeto for conhecida antecipadamente. #### Declarações de Ambiente (Ambient Declarations) Declarações de ambiente são arquivos que descrevem tipos para código JavaScript; eles têm o formato de nome de arquivo `.d.ts`. Geralmente são importados e usados para anotar bibliotecas JavaScript existentes ou para adicionar tipos a arquivos JS existentes em seu projeto. Muitos tipos de bibliotecas comuns podem ser encontrados em: [https://github.com/DefinitelyTyped/DefinitelyTyped/](https://github.com/DefinitelyTyped/DefinitelyTyped/) e podem ser instalados usando: ```shell npm install --save-dev @types/nome-da-biblioteca ``` Para suas Declarações de Ambiente definidas, você pode importar usando a referência de "barra tripla": ```typescript /// ``` Você pode usar Declarações de Ambiente até mesmo em arquivos JavaScript usando `// @ts-check`. A palavra-chave `declare` habilita definições de tipo para código JavaScript existente sem importá-lo, servindo como um marcador para tipos de outro arquivo ou globalmente. ### Verificação de Propriedades e Verificação de Excesso de Propriedades O TypeScript é baseado em um sistema de tipos estrutural, mas a verificação de excesso de propriedades é um recurso do TypeScript que permite verificar se um objeto tem exatamente as propriedades especificadas no tipo. A Verificação de Excesso de Propriedades é executada ao atribuir objetos literais a variáveis ou ao passá-los como argumentos para funções, por exemplo. ```typescript type X = { a: string; }; const y = { a: 'a', b: 'b' }; const x: X = y; // Válido por causa da tipagem estrutural const w: X = { a: 'a', b: 'b' }; // Inválido por causa da verificação de excesso de propriedades ``` ### Tipos Fracos (Weak Types) Um tipo é considerado fraco quando não contém nada além de um conjunto de todas as propriedades opcionais: ```typescript type X = { a?: string; b?: string; }; ``` O TypeScript considera um erro atribuir qualquer coisa a um tipo fraco quando não há sobreposição; por exemplo, o seguinte lança um erro: ```typescript type Options = { a?: string; b?: string; }; const fn = (options: Options) => undefined; fn({ c: 'c' }); // Inválido ``` Embora não recomendado, se necessário, é possível ignorar esta verificação usando asserção de tipo: ```typescript type Options = { a?: string; b?: string; }; const fn = (options: Options) => undefined; fn({ c: 'c' } as Options); // Válido ``` Ou adicionando `unknown` à assinatura de índice do tipo fraco: ```typescript type Options = { [prop: string]: unknown; a?: string; b?: string; }; const fn = (options: Options) => undefined; fn({ c: 'c' }); // Válido ``` ### Verificação Estrita de Objeto Literal (Freshness) A verificação estrita de objeto literal, às vezes chamada de "freshness", é um recurso do TypeScript que ajuda a capturar propriedades em excesso ou com erro de ortografia que, de outra forma, passariam despercebidas em verificações normais de tipo estrutural. Ao criar um objeto literal, o compilador TypeScript o considera "fresco" (fresh). Se o objeto literal for atribuído a uma variável ou passado como um parâmetro, o TypeScript lançará um erro se o objeto literal especificar propriedades que não existem no tipo de destino. No entanto, a "freshness" desaparece quando um objeto literal é alargado ou quando uma asserção de tipo é usada. Aqui estão alguns exemplos para ilustrar: ```typescript type X = { a: string }; type Y = { a: string; b: string }; let x: X; x = { a: 'a', b: 'b' }; // Verificação de Freshness: Atribuição inválida var y: Y; y = { a: 'a', bx: 'bx' }; // Verificação de Freshness: Atribuição inválida const fn = (x: X) => console.log(x.a); fn(x); fn(y); // Alargamento (Widening): Sem erros, estruturalmente compatível em termos de tipo fn({ a: 'a', bx: 'b' }); // Verificação de Freshness: Argumento inválido let c: X = { a: 'a' }; let d: Y = { a: 'a', b: '' }; c = d; // Alargamento: Sem verificação de Freshness ``` ### Inferência de Tipo O TypeScript pode inferir tipos quando nenhuma anotação é fornecida durante a: * Inicialização da variável. * Inicialização de membros. * Definição de valores padrão para parâmetros. * Tipo de retorno da função. Por exemplo: ```typescript let x = 'x'; // O tipo inferido é string ``` O compilador TypeScript analisa o valor ou expressão e determina seu tipo com base nas informações disponíveis. ### Inferências Mais Avançadas Quando várias expressões são usadas na inferência de tipo, o TypeScript procura pelos "melhores tipos comuns" (best common types). Por exemplo: ```typescript let x = [1, 'x', 1, null]; // O tipo inferido é: (string | number | null)[] ``` Se o compilador não conseguir encontrar os melhores tipos comuns, ele retorna um tipo de união. Por exemplo: ```typescript let x = [new RegExp('x'), new Date()]; // O tipo inferido é: (RegExp | Date)[] ``` O TypeScript utiliza a "tipagem contextual" baseada na localização da variável para inferir tipos. No exemplo a seguir, o compilador sabe que `e` é do tipo `MouseEvent` por causa do tipo de evento `click` definido no arquivo `lib.d.ts`, que contém declarações de ambiente para várias construções JavaScript comuns e o DOM: ```typescript window.addEventListener('click', function (e) {}); // O tipo inferido de e é MouseEvent ``` ### Alargamento de Tipo (Type Widening) O alargamento de tipo (type widening) é o processo no qual o TypeScript atribui um tipo a uma variável inicializada quando nenhuma anotação de tipo foi fornecida. Ele permite tipos de mais estreitos para mais amplos, mas não o contrário. No exemplo a seguir: ```typescript let x = 'x'; // O TypeScript infere como string, um tipo amplo let y: 'y' | 'x' = 'y'; // o tipo de y é uma união de tipos literais y = x; // Inválido: O tipo 'string' não pode ser atribuído ao tipo '"x" | "y"'. ``` O TypeScript atribui `string` a `x` com base no valor único fornecido durante a inicialização (`x`); este é um exemplo de alargamento. O TypeScript fornece maneiras de ter controle sobre o processo de alargamento, por exemplo, usando "const". ### Const O uso da palavra-chave `const` ao declarar uma variável resulta em uma inferência de tipo mais estreita no TypeScript. Por exemplo: ```typescript const x = 'x'; // O TypeScript infere o tipo de x como 'x', um tipo mais estreito let y: 'y' | 'x' = 'y'; y = x; // Válido: O tipo de x é inferido como 'x' ``` Ao usar `const` para declarar a variável x, seu tipo é estreitado para o valor literal específico 'x'. Como o tipo de x é estreitado, ele pode ser atribuído à variável y sem nenhum erro. A razão pela qual o tipo pode ser inferido é porque as variáveis `const` não podem ser reatribuídas, portanto seu tipo pode ser estreitado para um tipo literal específico, neste caso, o tipo literal 'x'. #### Modificador Const em Parâmetros de Tipo A partir da versão 5.0 do TypeScript, é possível especificar o atributo `const` em um parâmetro de tipo genérico. Isso permite inferir o tipo mais preciso possível. Vejamos um exemplo sem usar `const`: ```typescript function identity(value: T) { // Sem const aqui return value; } const values = identity({ a: 'a', b: 'b' }); // O tipo inferido é: { a: string; b: string; } ``` Como você pode ver, as propriedades `a` e `b` são inferidas com o tipo `string`. Agora, vejamos a diferença com a versão `const`: ```typescript function identity(value: T) { // Usando modificador const em parâmetros de tipo return value; } const values = identity({ a: 'a', b: 'b' }); // O tipo inferido é: { a: "a"; b: "b"; } ``` Agora podemos ver que as propriedades `a` e `b` são inferidas como `const`, portanto `a` e `b` são tratados como literais de string em vez de apenas tipos `string`. #### Asserção Const (Const assertion) Este recurso permite declarar uma variável com um tipo literal mais preciso baseado em seu valor de inicialização, sinalizando ao compilador que o valor deve ser tratado como um literal imutável. Aqui estão alguns exemplos: Em uma única propriedade: ```typescript const v = { x: 3 as const, }; v.x = 3; ``` Em um objeto inteiro: ```typescript const v = { x: 1, y: 2, } as const; ``` Isso pode ser particularmente útil ao definir o tipo para uma tupla: ```typescript const x = [1, 2, 3]; // number[] const y = [1, 2, 3] as const; // Tupla de readonly [1, 2, 3] ``` ### Anotação de Tipo Explícita Podemos ser específicos e passar um tipo; no exemplo a seguir, a propriedade `x` é do tipo `number`: ```typescript const v = { x: 1, // Tipo inferido: number (alargamento) }; v.x = 3; // Válido ``` Podemos tornar a anotação de tipo mais específica usando uma união de tipos literais: ```typescript const v: { x: 1 | 2 | 3 } = { x: 1, // x agora é uma união de tipos literais: 1 | 2 | 3 }; v.x = 3; // Válido v.x = 100; // Inválido ``` ### Estreitamento de Tipo (Type Narrowing) O Estreitamento de Tipo (Type Narrowing) é o processo no TypeScript onde um tipo geral é estreitado para um tipo mais específico. Isso ocorre quando o TypeScript analisa o código e determina que certas condições ou operações podem refinar a informação do tipo. O estreitamento de tipos pode ocorrer de diferentes maneiras, incluindo: #### Condições Ao usar instruções condicionais, como `if` ou `switch`, o TypeScript pode estreitar o tipo com base no resultado da condição. Por exemplo: ```typescript let x: number | undefined = 10; if (x !== undefined) { x += 100; // O tipo é number, que foi estreitado pela condição } ``` #### Lançando ou retornando Lançar um erro ou retornar cedo de uma ramificação pode ser usado para ajudar o TypeScript a estreitar um tipo. Por exemplo: ```typescript let x: number | undefined = 10; if (x === undefined) { throw 'erro'; } x += 100; ``` Outras formas de estreitar tipos no TypeScript incluem: * Operador `instanceof`: Usado para verificar se um objeto é uma instância de uma classe específica. * Operador `in`: Usado para verificar se uma propriedade existe em um objeto. * Operador `typeof`: Usado para verificar o tipo de um valor em tempo de execução. * Funções integradas como `Array.isArray()`: Usadas para verificar se um valor é um array. #### União Discriminada O uso de uma "União Discriminada" é um padrão no TypeScript onde uma "tag" explícita é adicionada aos objetos para distinguir entre diferentes tipos dentro de uma união. Este padrão também é conhecido como "união tagueada" (tagged union). No exemplo a seguir, a "tag" é representada pela propriedade "type": ```typescript type A = { type: 'type_a'; value: number }; type B = { type: 'type_b'; value: string }; const x = (input: A | B): string | number => { switch (input.type) { case 'type_a': return input.value + 100; // o tipo é A case 'type_b': return input.value + 'extra'; // o tipo é B } }; ``` #### Proteções de Tipo Definidas pelo Usuário (User-Defined Type Guards) Em casos onde o TypeScript não é capaz de determinar um tipo, é possível escrever uma função auxiliar conhecida como "proteção de tipo definida pelo usuário" (user-defined type guard). No exemplo a seguir, utilizaremos um Predicado de Tipo para estreitar o tipo após aplicar certa filtragem: ```typescript const data = ['a', null, 'c', 'd', null, 'f']; const r1 = data.filter(x => x != null); // O tipo é (string | null)[], o TypeScript não foi capaz de inferir o tipo corretamente const isValid = (item: string | null): item is string => item !== null; // Protetor de tipo customizado const r2 = data.filter(isValid); // O tipo está correto agora string[], ao usar o protetor de tipo predicado conseguimos estreitar o tipo ``` ================================================ FILE: website/src/content/docs/pt-br/book/extending-types.md ================================================ --- title: Estendendo Tipos sidebar: order: 15 label: 15. Estendendo Tipos --- É possível estender uma `interface` (copiar membros de outro tipo): ```typescript interface X { a: string; } interface Y extends X { b: string; } ``` Também é possível estender de múltiplos tipos: ```typescript interface A { a: string; } interface B { b: string; } interface Y extends A, B { y: string; } ``` A palavra-chave `extends` funciona apenas em interfaces e classes; para tipos, use uma interseção: ```typescript type A = { a: number; }; type B = { b: number; }; type C = A & B; ``` É possível estender um tipo usando uma inferência, mas não o contrário: ```typescript type A = { a: string; }; interface B extends A { b: string; } ``` ================================================ FILE: website/src/content/docs/pt-br/book/fixed-length-tuple.md ================================================ --- title: Tupla de Comprimento Fixo sidebar: order: 30 label: 30. Tupla de Comprimento Fixo --- Uma Tupla de Comprimento Fixo é um tipo específico de tupla que impõe um número fixo de elementos de tipos específicos e proíbe quaisquer modificações no comprimento da tupla uma vez definida. Tuplas de Comprimento Fixo são úteis quando você precisa representar uma coleção de valores com um número específico de elementos e tipos específicos, e deseja garantir que o comprimento e os tipos da tupla não possam ser alterados inadvertidamente. ```typescript const x = [10, 'hello'] as const; x.push(2); // Erro ``` ================================================ FILE: website/src/content/docs/pt-br/book/generics.md ================================================ --- title: Genéricos (Generics) sidebar: order: 55 label: 55. Genéricos (Generics) --- Os genéricos permitem que você crie componentes e funções reutilizáveis que podem trabalhar com múltiplos tipos. Com os genéricos, você pode parametrizar tipos, funções e interfaces, permitindo que operem em diferentes tipos sem especificá-los explicitamente de antemão. Os genéricos permitem tornar o código mais flexível e reutilizável. ### Tipo Genérico Para definir um tipo genérico, você usa colchetes angulares (`<>`) para especificar os parâmetros de tipo, por exemplo: ```typescript function identity(arg: T): T { return arg; } const a = identity('x'); const b = identity(123); const getLen = (data: ReadonlyArray) => data.length; const len = getLen([1, 2, 3]); ``` ### Classes Genéricas Os genéricos também podem ser aplicados a classes, permitindo que trabalhem com múltiplos tipos por meio de parâmetros de tipo. Isso é útil para criar definições de classe reutilizáveis que podem operar em diferentes tipos de dados mantendo a segurança de tipo. ```typescript class Container { private item: T; constructor(item: T) { this.item = item; } getItem(): T { return this.item; } } const numberContainer = new Container(123); console.log(numberContainer.getItem()); // 123 const stringContainer = new Container('hello'); console.log(stringContainer.getItem()); // hello ``` ### Restrições Genéricas (Generic Constraints) Parâmetros genéricos podem ser restringidos usando a palavra-chave `extends` seguida por um tipo ou interface que o parâmetro de tipo deve satisfazer. No exemplo a seguir, T deve conter uma propriedade `length` para ser válido: ```typescript const printLen = (value: T): void => { console.log(value.length); }; printLen('Hello'); // 5 printLen([1, 2, 3]); // 3 printLen({ length: 10 }); // 10 printLen(123); // Inválido ``` Um recurso interessante de genéricos introduzido na versão 3.4 RC é a inferência de tipo de função de ordem superior, que introduziu argumentos de tipo genérico propagados: ```typescript declare function pipe
( ab: (...args: A) => B, bc: (b: B) => C ): (...args: A) => C; declare function list(a: T): T[]; declare function box(x: V): { value: V }; const listBox = pipe(list, box); // (a: T) => { value: T[] } const boxList = pipe(box, list); // (x: V) => { value: V }[] ``` Essa funcionalidade permite uma programação de estilo sem pontos (pointfree) com segurança de tipo mais fácil, o que é comum na programação funcional. ### Estreitamento Contextual Genérico O estreitamento contextual (contextual narrowing) para genéricos é o mecanismo no TypeScript que permite ao compilador estreitar o tipo de um parâmetro genérico com base no contexto em que é usado. É útil ao trabalhar com tipos genéricos em declarações condicionais: ```typescript function process(value: T): void { if (typeof value === 'string') { // O valor é estreitado para o tipo 'string' console.log(value.length); } else if (typeof value === 'number') { // O valor é estreitado para o tipo 'number' console.log(value.toFixed(2)); } } process('hello'); // 5 process(3.14159); // 3.14 ``` ================================================ FILE: website/src/content/docs/pt-br/book/getting-started-with-typescript.md ================================================ --- title: Começando com TypeScript sidebar: order: 8 label: 8. Começando com TypeScript --- ### Instalação O Visual Studio Code oferece excelente suporte para a linguagem TypeScript, mas não inclui o compilador TypeScript. Para instalar o compilador TypeScript, você pode usar um gerenciador de pacotes como npm ou yarn: ```shell npm install typescript --save-dev ``` ou ```shell yarn add typescript --dev ``` Certifique-se de realizar o commit do arquivo de bloqueio (lockfile) gerado para garantir que cada membro da equipe use a mesma versão do TypeScript. Para executar o compilador TypeScript, você pode usar os seguintes comandos: ```shell npx tsc ``` ou ```shell yarn tsc ``` Recomenda-se instalar o TypeScript por projeto em vez de globalmente, pois fornece um processo de construção mais previsível. No entanto, para ocasiões pontuais, você pode usar o seguinte comando: ```shell npx tsc ``` ou instalá-lo globalmente: ```shell npm install -g typescript ``` Se você estiver usando o Microsoft Visual Studio, pode obter o TypeScript como um pacote no NuGet para seus projetos MSBuild. No Console do Gerenciador de Pacotes NuGet, execute o seguinte comando: ```shell Install-Package Microsoft.TypeScript.MSBuild ``` Durante a instalação do TypeScript, dois executáveis são instalados: "tsc" como o compilador TypeScript e "tsserver" como o servidor autônomo do TypeScript. O servidor autônomo contém o compilador e os serviços de linguagem que podem ser utilizados por editores e IDEs para fornecer completamento inteligente de código. Além disso, existem vários transpiladores compatíveis com TypeScript disponíveis, como Babel (via um plugin) ou swc. Esses transpiladores podem ser usados para converter código TypeScript em outras linguagens ou versões de destino. ### Configuração O TypeScript pode ser configurado usando as opções da CLI do tsc ou utilizando um arquivo de configuração dedicado chamado tsconfig.json localizado na raiz do projeto. Para gerar um arquivo tsconfig.json pré-preenchido com as configurações recomendadas, você pode usar o seguinte comando: ```shell tsc --init ``` Ao executar o comando `tsc` localmente, o TypeScript compilará o código usando a configuração especificada no arquivo tsconfig.json mais próximo. Aqui estão alguns exemplos de comandos da CLI que rodam com as configurações padrão: ```shell tsc main.ts // Compila um arquivo específico (main.ts) para JavaScript tsc src/*.ts // Compila todos os arquivos .ts na pasta 'src' para JavaScript tsc app.ts util.ts --outfile index.js // Compila dois arquivos TypeScript (app.ts e util.ts) em um único arquivo JavaScript (index.js) ``` ### Arquivo de Configuração do TypeScript Um arquivo tsconfig.json é usado para configurar o Compilador TypeScript (tsc). Geralmente, ele é adicionado à raiz do projeto, junto com o arquivo `package.json`. Notas: * tsconfig.json aceita comentários, mesmo estando no formato json. * É aconselhável usar este arquivo de configuração em vez das opções de linha de comando. No link a seguir você encontra a documentação completa e seu esquema: [https://www.typescriptlang.org/tsconfig](https://www.typescriptlang.org/tsconfig) [https://www.typescriptlang.org/tsconfig/](https://www.typescriptlang.org/tsconfig/) A seguir, apresentamos uma lista das configurações comuns e úteis: #### target A propriedade "target" é usada para especificar qual versão do JavaScript ECMAScript seu TypeScript deve emitir/compilar. Para navegadores modernos, o ES6 é uma boa opção; para navegadores mais antigos, o ES5 é recomendado. #### lib A propriedade "lib" é usada para especificar quais arquivos de biblioteca incluir no tempo de compilação. O TypeScript inclui automaticamente APIs para recursos especificados na propriedade "target", mas é possível omitir ou escolher bibliotecas específicas para necessidades particulares. Por exemplo, se você estiver trabalhando em um projeto de servidor, pode excluir a biblioteca "DOM", que é útil apenas em um ambiente de navegador. #### strict A propriedade "strict" habilita garantias mais fortes e aumenta a segurança de tipos. É aconselhável incluir sempre esta propriedade no arquivo tsconfig.json do seu projeto. Habilitar a propriedade "strict" permite que o TypeScript possa: * Emitir código usando "use strict" para cada arquivo de origem. * Considerar "null" e "undefined" no processo de verificação de tipos. * Desabilitar o uso do tipo "any" quando não houver anotações de tipo. * Levantar um erro sobre o uso da expressão "this", que de outra forma implicaria o tipo "any". #### module A propriedade "module" define o sistema de módulo suportado para o programa compilado. Durante o tempo de execução, um carregador de módulo é usado para localizar e executar dependências com base no sistema de módulo especificado. Os carregadores de módulos mais comuns usados no JavaScript são o CommonJS do Node.js para aplicações do lado do servidor e o RequireJS para módulos AMD em aplicações web baseadas em navegador. O TypeScript pode emitir código para vários sistemas de módulos, incluindo UMD, System, ESNext, ES2015/ES6 e ES2020. Nota: O sistema de módulos deve ser escolhido com base no ambiente de destino e no mecanismo de carregamento de módulos disponível nesse ambiente. #### moduleResolution A propriedade "moduleResolution" especifica a estratégia de resolução de módulos. Use "node" para código TypeScript moderno; a estratégia "classic" é usada apenas para versões antigas do TypeScript (antes da 1.6). #### esModuleInterop A propriedade "esModuleInterop" permite a importação padrão de módulos CommonJS que não exportaram usando a propriedade "default"; esta propriedade fornece um shim para garantir a compatibilidade no JavaScript emitido. Após habilitar esta opção, podemos usar `import MyLibrary from "my-library"` em vez de `import * as MyLibrary from "my-library"`. #### jsx A propriedade "jsx" aplica-se apenas a arquivos .tsx usados no ReactJS e controla como as construções JSX são compiladas em JavaScript. Uma opção comum é "preserve", que compilará para um arquivo .jsx mantendo o JSX inalterado para que ele possa ser passado para diferentes ferramentas, como o Babel, para transformações posteriores. #### skipLibCheck A propriedade "skipLibCheck" evitará que o TypeScript verifique os tipos de todos os pacotes de terceiros importados. Esta propriedade reduzirá o tempo de compilação de um projeto. O TypeScript ainda verificará seu código em relação às definições de tipo fornecidas por esses pacotes. #### files A propriedade "files" indica ao compilador uma lista de arquivos que devem sempre ser incluídos no programa. #### include A propriedade "include" indica ao compilador uma lista de arquivos que gostaríamos de incluir. Esta propriedade permite padrões semelhantes a glob, como "\**" para qualquer subdiretório, "*" para qualquer nome de arquivo e "?" para caracteres opcionais. #### exclude A propriedade "exclude" indica ao compilador uma lista de arquivos que não devem ser incluídos na compilação. Isso pode incluir arquivos como "node_modules" ou arquivos de teste. Nota: tsconfig.json permite comentários. ### importHelpers O TypeScript usa código auxiliar ao gerar código para certos recursos avançados ou de JavaScript com "down-leveled". Por padrão, esses auxiliares são duplicados nos arquivos que os utilizam. A opção `importHelpers` importa esses auxiliares do módulo `tslib`, tornando a saída do JavaScript mais eficiente. ### Conselhos para Migração para TypeScript Para projetos grandes, recomenda-se adotar uma transição gradual onde o código TypeScript e JavaScript coexistirão inicialmente. Apenas projetos pequenos podem ser migrados para TypeScript de uma só vez. O primeiro passo desta transição é introduzir o TypeScript no processo da cadeia de construção. Isso pode ser feito usando a opção de compilador "allowJs", que permite que arquivos .ts e .tsx coexistam com arquivos JavaScript existentes. Como o TypeScript voltará para um tipo "any" para uma variável quando não puder inferir o tipo dos arquivos JavaScript, recomenda-se desabilitar "noImplicitAny" em suas opções de compilador no início da migração. O segundo passo é garantir que seus testes JavaScript funcionem junto com os arquivos TypeScript, para que você possa executar testes conforme converte cada módulo. Se estiver usando Jest, considere usar o `ts-jest`, que permite testar projetos TypeScript com Jest. O terceiro passo é incluir declarações de tipo para bibliotecas de terceiros em seu projeto. Essas declarações podem ser encontradas empacotadas ou no DefinitelyTyped. Você pode pesquisar por elas usando [https://www.typescriptlang.org/dt/search](https://www.typescriptlang.org/dt/search) e instalá-las usando: ```shell npm install --save-dev @types/package-name ``` ou ```shell yarn add --dev @types/package-name ``` O quarto passo é migrar módulo por módulo com uma abordagem de baixo para cima, seguindo seu Gráfico de Dependências começando pelas folhas. A ideia é começar convertendo Módulos que não dependem de outros Módulos. Para visualizar os gráficos de dependência, você pode usar a ferramenta "madge". Bons módulos candidatos para essas conversões iniciais são funções utilitárias e código relacionado a APIs ou especificações externas. É possível gerar automaticamente definições de tipo TypeScript a partir de contratos Swagger, GraphQL ou esquemas JSON para serem incluídos em seu projeto. Quando não houver especificações ou esquemas oficiais disponíveis, você pode gerar tipos a partir de dados brutos, como JSON retornado por um servidor. No entanto, recomenda-se gerar tipos a partir de especificações em vez de dados para evitar perder casos extremos. Durante a migração, evite a refatoração de código e concentre-se apenas em adicionar tipos aos seus módulos. O quinto passo é habilitar o "noImplicitAny", que forçará que todos os tipos sejam conhecidos e definidos, proporcionando uma melhor experiência de TypeScript para seu projeto. Durante a migração, você pode usar a diretiva `@ts-check`, que habilita a verificação de tipos do TypeScript em um arquivo JavaScript. Esta diretiva fornece uma versão flexível de verificação de tipos e pode ser usada inicialmente para identificar problemas em arquivos JavaScript. Quando o `@ts-check` é incluído em um arquivo, o TypeScript tentará deduzir definições usando comentários no estilo JSDoc. No entanto, considere usar anotações JSDoc apenas em um estágio muito inicial da migração. Considere manter o valor padrão de `noEmitOnError` no seu tsconfig.json como false. Isso permitirá gerar o código-fonte JavaScript mesmo se erros forem relatados. ================================================ FILE: website/src/content/docs/pt-br/book/index-signatures.md ================================================ --- title: Assinaturas de Índice (Index Signatures) sidebar: order: 14 label: 14. Assinaturas de Índice (Index Signatures) --- No TypeScript, podemos usar como assinatura de índice `string`, `number` e `symbol`: ```typescript type K = { [name: string | number]: string; }; const k: K = { x: 'x', 1: 'b' }; console.log(k['x']); console.log(k[1]); console.log(k['1']); // Mesmo resultado que k[1] ``` Observe que o JavaScript converte automaticamente um índice com `number` em um índice com `string`, portanto `k[1]` ou `k["1"]` retornam o mesmo valor. ================================================ FILE: website/src/content/docs/pt-br/book/infer-type-inference-in-conditional-types.md ================================================ --- title: infer Inferência de Tipo em Tipos Condicionais sidebar: order: 41 label: 41. infer Inferência de Tipo em Tipos Condicionais --- A palavra-chave `infer` é usada em tipos condicionais para inferir (extrair) o tipo de um parâmetro genérico de um tipo que depende dele. Isso permite escrever definições de tipo mais flexíveis e reutilizáveis. ```typescript type ElementType = T extends (infer U)[] ? U : never; type Numbers = ElementType; // number type Strings = ElementType; // string ``` ================================================ FILE: website/src/content/docs/pt-br/book/interface-and-type.md ================================================ --- title: Interface e Type sidebar: order: 48 label: 48. Interface e Type --- ### Sintaxe Comum No TypeScript, as interfaces definem a estrutura de objetos, especificando os nomes e tipos de propriedades ou métodos que um objeto deve possuir. A sintaxe comum para definir uma interface no TypeScript é a seguinte: ```typescript interface InterfaceName { property1: Type1; // ... method1(arg1: ArgType1, arg2: ArgType2): ReturnType; // ... } ``` Da mesma forma para a definição de tipo (type): ```typescript type TypeName = { property1: Type1; // ... method1(arg1: ArgType1, arg2: ArgType2): ReturnType; // ... }; ``` `interface InterfaceName` ou `type TypeName`: Define o nome da interface/tipo. `property1`: `Type1`: Especifica as propriedades da interface junto com seus tipos correspondentes. Múltiplas propriedades podem ser definidas, cada uma separada por um ponto e vírgula. `method1(arg1: ArgType1, arg2: ArgType2): ReturnType;`: Especifica os métodos da interface. Métodos são definidos com seus nomes, seguidos por uma lista de parâmetros entre parênteses e o tipo de retorno. Múltiplos métodos podem ser definidas, cada uma separada por um ponto e vírgula. Exemplo de interface: ```typescript interface Person { name: string; age: number; greet(): void; } ``` Exemplo de tipo: ```typescript type TypeName = { property1: string; method1(arg1: string, arg2: string): string; }; ``` No TypeScript, os tipos são usados para definir a forma dos dados e impor a verificação de tipos. Existem várias sintaxes comuns para definir tipos, dependendo do caso de uso específico. Aqui estão alguns exemplos: ### Tipos Básicos ```typescript let myNumber: number = 123; // tipo number let myBoolean: boolean = true; // tipo boolean let myArray: string[] = ['a', 'b']; // array de strings let myTuple: [string, number] = ['a', 123]; // tupla ``` ### Objetos e Interfaces ```typescript const x: { name: string; age: number } = { name: 'Simon', age: 7 }; ``` ### Tipos União e Interseção ```typescript type MyType = string | number; // Tipo União (Union type) let myUnion: MyType = 'hello'; // Pode ser uma string myUnion = 123; // Ou um número type TypeA = { name: string }; type TypeB = { age: number }; type CombinedType = TypeA & TypeB; // Tipo Interseção (Intersection type) let myCombined: CombinedType = { name: 'John', age: 25 }; // Objeto com as propriedades name e age ``` ================================================ FILE: website/src/content/docs/pt-br/book/intersection-types.md ================================================ --- title: Tipos de Interseção sidebar: order: 32 label: 32. Tipos de Interseção --- Um Tipo de Interseção (Intersection Type) é um tipo que representa um valor que possui todas as propriedades de dois ou mais tipos. Tipos de Interseção são denotados usando o símbolo `&` entre cada tipo. ```typescript type X = { a: string; }; type Y = { b: string; }; type J = X & Y; // Interseção const j: J = { a: 'a', b: 'b', }; ``` ================================================ FILE: website/src/content/docs/pt-br/book/introduction.md ================================================ --- title: Introdução sidebar: order: 5 label: 5. Introdução --- Bem-vindo ao Livro Conciso de TypeScript! Este guia o equipa com conhecimentos essenciais e habilidades práticas para o desenvolvimento eficaz em TypeScript. Descubra conceitos e técnicas fundamentais para escrever código limpo e robusto. Seja você um iniciante ou um desenvolvedor experiente, este livro serve tanto como um guia abrangente quanto como uma referência prática para aproveitar o poder do TypeScript em seus projetos. Este livro cobre o TypeScript 5.2. ================================================ FILE: website/src/content/docs/pt-br/book/literal-inference.md ================================================ --- title: Inferência Literal sidebar: order: 17 label: 17. Inferência Literal --- A Inferência Literal é um recurso do TypeScript que permite que o tipo de uma variável ou parâmetro seja inferido com base em seu valor. No exemplo a seguir, podemos ver que o TypeScript considera `x` um tipo literal, pois o valor não pode ser alterado posteriormente, enquanto `y` é inferido como string, pois pode ser modificado posteriormente. ```typescript const x = 'x'; // Tipo literal de 'x', porque este valor não pode ser alterado let y = 'y'; // Tipo string, pois podemos alterar este valor ``` No exemplo a seguir, podemos ver que `o.x` foi inferido como uma `string` (e não um literal de `a`), pois o TypeScript considera que o valor pode ser alterado posteriormente. ```typescript type X = 'a' | 'b'; let o = { x: 'a', // Esta é uma string mais ampla (wider string) }; const fn = (x: X) => `${x}-foo`; console.log(fn(o.x)); // Argument of type 'string' is not assignable to parameter of type 'X' ``` Como você pode observar, o código lança um erro ao passar `o.x` para `fn`, pois X é um tipo mais estreito (narrower). Podemos resolver este problema usando asserção de tipo com `const` ou o tipo `X`: ```typescript let o = { x: 'a' as const, }; ``` ou: ```typescript let o = { x: 'a' as X, }; ``` ================================================ FILE: website/src/content/docs/pt-br/book/literal-types.md ================================================ --- title: Tipos Literais sidebar: order: 16 label: 16. Tipos Literais --- Um Tipo Literal é um conjunto de elemento único a partir de um tipo coletivo; ele define um valor exato que é um primitivo do JavaScript. Os Tipos Literais no TypeScript são números, strings e booleanos. Exemplo de literais: ```typescript const a = 'a'; // Tipo literal de string const b = 1; // Tipo literal numérico const c = true; // Tipo literal booleano ``` Tipos Literais de String, Numéricos e Booleanos são usados em uniões, protetores de tipo (type guards) e apelidos de tipo (type aliases). No exemplo a seguir, você pode ver um apelido de tipo de união. `O` consiste apenas nos valores especificados; nenhuma outra string é válida: ```typescript type O = 'a' | 'b' | 'c'; ``` ================================================ FILE: website/src/content/docs/pt-br/book/mapped-type-modifiers.md ================================================ --- title: Modificadores de Tipos Mapeados sidebar: order: 38 label: 38. Modificadores de Tipos Mapeados --- Os Modificadores de Tipos Mapeados no TypeScript permitem a transformação de propriedades dentro de um tipo existente: * `readonly` ou `+readonly`: Torna uma propriedade no tipo mapeado como somente leitura. * `-readonly`: Permite que uma propriedade no tipo mapeado seja mutável. * `?`: Designa uma propriedade no tipo mapeado como opcional. Exemplos: ```typescript type ReadOnly = { readonly [P in keyof T]: T[P] }; // Todas as propriedades marcadas como somente leitura type Mutable = { -readonly [P in keyof T]: T[P] }; // Todas as propriedades marcadas como mutáveis type MyPartial = { [P in keyof T]?: T[P] }; // Todas as propriedades marcadas como opcionais ``` ================================================ FILE: website/src/content/docs/pt-br/book/mapped-types.md ================================================ --- title: Tipos Mapeados sidebar: order: 37 label: 37. Tipos Mapeados --- Tipos Mapeados (Mapped Types) no TypeScript permitem criar novos tipos baseados em um tipo existente, transformando cada propriedade por meio de uma função de mapeamento. Ao mapear tipos existentes, você pode criar novos tipos que representam a mesma informação em um formato diferente. Para criar um tipo mapeado, você acessa as propriedades de um tipo existente usando o operador `keyof` e as altera para produzir um novo tipo. No exemplo a seguir: ```typescript type MyMappedType = { [P in keyof T]: T[P][]; }; type MyType = { foo: string; bar: number; }; type MyNewType = MyMappedType; const x: MyNewType = { foo: ['hello', 'world'], bar: [1, 2, 3], }; ``` definimos `MyMappedType` para mapear sobre as propriedades de `T`, criando um novo tipo com cada propriedade sendo um array de seu tipo original. Usando isso, criamos `MyNewType` para representar a mesma informação que `MyType`, mas com cada propriedade como um array. ================================================ FILE: website/src/content/docs/pt-br/book/merging-and-extension.md ================================================ --- title: Mesclagem e Extensão sidebar: order: 52 label: 52. Mesclagem e Extensão --- Mesclagem (merging) e extensão (extension) referem-se a dois conceitos diferentes relacionados ao trabalho com tipos e interfaces. A mesclagem permite combinar várias declarações do mesmo nome em uma única definição, por exemplo, quando você define uma interface com o mesmo nome várias vezes: ```typescript interface X { a: string; } interface X { b: number; } const person: X = { a: 'a', b: 7, }; ``` A extensão refere-se à capacidade de estender ou herdar de tipos ou interfaces existentes para criar novos. É um mecanismo para adicionar propriedades ou métodos adicionais a um tipo existente sem modificar sua definição original. Exemplo: ```typescript interface Animal { name: string; eat(): void; } interface Bird extends Animal { sing(): void; } const dog: Bird = { name: 'Bird 1', eat() { console.log('Comendo'); }, sing() { console.log('Cantando'); }, }; ``` ================================================ FILE: website/src/content/docs/pt-br/book/named-tuple-type-labeled.md ================================================ --- title: Tipo Tupla Nomeado (Rotulado) sidebar: order: 29 label: 29. Tipo Tupla Nomeado (Rotulado) --- Os tipos tupla podem incluir rótulos (labels) ou nomes opcionais para cada elemento. Esses rótulos são para legibilidade e assistência de ferramentas, e não afetam as operações que você pode realizar com eles. ```typescript type T = string; type Tuple1 = [T, T]; type Tuple2 = [a: T, b: T]; type Tuple3 = [a: T, T]; // Tupla Nomeada mais Tupla Anônima ``` ================================================ FILE: website/src/content/docs/pt-br/book/namespacing.md ================================================ --- title: Namespacing sidebar: order: 57 label: 57. Namespacing --- No TypeScript, os namespaces são usados para organizar o código em contêineres lógicos, evitando colisões de nomes e fornecendo uma maneira de agrupar códigos relacionados. O uso das palavras-chave `export` permite o acesso ao namespace em módulos externos. ```typescript export namespace MyNamespace { export interface MyInterface1 { prop1: boolean; } export interface MyInterface2 { prop2: string; } } const a: MyNamespace.MyInterface1 = { prop1: true, }; ``` ================================================ FILE: website/src/content/docs/pt-br/book/narrowing.md ================================================ --- title: Estreitamento (Narrowing) sidebar: order: 20 label: 20. Estreitamento (Narrowing) --- O estreitamento (narrowing) no TypeScript é o processo de refinar o tipo de uma variável dentro de um bloco condicional. Isso é útil ao trabalhar com tipos de união, onde uma variável pode ter mais de um tipo. O TypeScript reconhece várias maneiras de estreitar o tipo: ### typeof type guards O protetor de tipo (type guard) `typeof` é um protetor de tipo específico no TypeScript que verifica o tipo de uma variável com base em seu tipo JavaScript integrado. ```typescript const fn = (x: number | string) => { if (typeof x === 'number') { return x + 1; // x é number } return -1; }; ``` ### Estreitamento de veracidade (Truthiness narrowing) O estreitamento de veracidade (truthiness narrowing) no TypeScript funciona verificando se uma variável é verdadeira (truthy) ou falsa (falsy) para estreitar seu tipo adequadamente. ```typescript const toUpperCase = (name: string | null) => { if (name) { return name.toUpperCase(); } else { return null; } }; ``` ### Estreitamento de igualdade (Equality narrowing) O estreitamento de igualdade (equality narrowing) no TypeScript funciona verificando se uma variável é igual a um valor específico ou não, para estreitar seu tipo adequadamente. É usado em conjunto com instruções `switch` e operadores de igualdade como `===`, `!==`, `==` e `!=` para estreitar os tipos. ```typescript const checkStatus = (status: 'success' | 'error') => { switch (status) { case 'success': return true; case 'error': return null; } }; ``` ### Estreitamento com operador In O estreitamento com o operador `in` no TypeScript é uma forma de estreitar o tipo de uma variável com base na existência de uma propriedade dentro do tipo da variável. ```typescript type Dog = { name: string; breed: string; }; type Cat = { name: string; likesCream: boolean; }; const getAnimalType = (pet: Dog | Cat) => { if ('breed' in pet) { return 'dog'; } else { return 'cat'; } }; ``` ### Estreitamento com instanceof O estreitamento com o operador `instanceof` no TypeScript é uma forma de estreitar o tipo de uma variável com base em sua função construtora, verificando se um objeto é uma instância de uma determinada classe ou interface. ```typescript class Square { constructor(public width: number) {} } class Rectangle { constructor( public width: number, public height: number ) {} } function area(shape: Square | Rectangle) { if (shape instanceof Square) { return shape.width * shape.width; } else { return shape.width * shape.height; } } const square = new Square(5); const rectangle = new Rectangle(5, 10); console.log(area(square)); // 25 console.log(area(rectangle)); // 50 ``` ================================================ FILE: website/src/content/docs/pt-br/book/never-type.md ================================================ --- title: Tipo never sidebar: order: 47 label: 47. Tipo never --- O tipo `never` representa valores que nunca ocorrem. É usado para denotar funções ou expressões que nunca retornam ou que lançam um erro. Por exemplo, um loop infinito: ```typescript const infiniteLoop = (): never => { while (true) { // faz algo } }; ``` Lançando um erro: ```typescript const throwError = (message: string): never => { throw new Error(message); }; ``` O tipo `never` é útil para garantir a segurança de tipos e capturar possíveis erros em seu código. Ele ajuda o TypeScript a analisar e inferir tipos mais precisos quando usado em combinação com outros tipos e instruções de fluxo de controle, por exemplo: ```typescript type Direction = 'up' | 'down'; const move = (direction: Direction): void => { switch (direction) { case 'up': // move para cima break; case 'down': // move para baixo break; default: const exhaustiveCheck: never = direction; throw new Error(`Direção não tratada: ${exhaustiveCheck}`); } }; ``` ================================================ FILE: website/src/content/docs/pt-br/book/object-types.md ================================================ --- title: Tipos de Objeto sidebar: order: 27 label: 27. Tipos de Objeto --- No TypeScript, os tipos de objeto descrevem a forma de um objeto. Eles especificam os nomes e tipos das propriedades do objeto, bem como se essas propriedades são obrigatórias ou opcionais. No TypeScript, você pode definir tipos de objeto de duas maneiras principais: Interface, que define a forma de um objeto especificando os nomes, tipos e a opcionalidade de suas propriedades. ```typescript interface User { name: string; age: number; email?: string; } ``` Apelido de tipo (type alias), semelhante a uma interface, define a forma de um objeto. No entanto, ele também pode criar um novo tipo personalizado baseado em um tipo existente ou em uma combinação de tipos existentes. Isso inclui definir tipos de união, tipos de interseção e outros tipos complexos. ```typescript type Point = { x: number; y: number; }; ``` Também é possível definir um tipo anonimamente: ```typescript const sum = (x: { a: number; b: number }) => x.a + x.b; console.log(sum({ a: 5, b: 1 })); ``` ================================================ FILE: website/src/content/docs/pt-br/book/optional-properties.md ================================================ --- title: Propriedades Opcionais sidebar: order: 12 label: 12. Propriedades Opcionais --- Um objeto pode especificar Propriedades Opcionais adicionando um ponto de interrogação `?` ao final do nome da propriedade: ```typescript type X = { a: number; b?: number; // Opcional }; ``` É possível especificar um valor padrão quando uma propriedade é opcional: ```typescript type X = { a: number; b?: number; }; const x = ({ a, b = 100 }: X) => a + b; ``` ================================================ FILE: website/src/content/docs/pt-br/book/others.md ================================================ --- title: Outros sidebar: order: 61 label: 61. Outros --- ### Erros e Tratamento de Exceções O TypeScript permite capturar e tratar erros usando os mecanismos padrão de tratamento de erros do JavaScript: Blocos Try-Catch-Finally: ```typescript try { // Código que pode lançar um erro } catch (error) { // Trata o erro } finally { // Código que sempre executa, finally é opcional } ``` Você também pode tratar diferentes tipos de erro: ```typescript try { // Código que pode lançar diferentes tipos de erros } catch (error) { if (error instanceof TypeError) { // Trata TypeError } else if (error instanceof RangeError) { // Trata RangeError } else { // Trata outros erros } } ``` Tipos de Erro Personalizados: É possível especificar erros mais específicos estendendo a `class` Error: ```typescript class CustomError extends Error { constructor(message: string) { super(message); this.name = 'CustomError'; } } throw new CustomError('Este é um erro personalizado.'); ``` ### Classes Mixin (Mixin classes) As classes mixin permitem combinar e compor comportamentos de múltiplas classes em uma única classe. Elas fornecem uma maneira de reutilizar e estender funcionalidades sem a necessidade de cadeias de herança profundas. ```typescript abstract class Identifiable { name: string = ''; logId() { console.log('id:', this.name); } } abstract class Selectable { selected: boolean = false; select() { this.selected = true; console.log('Select'); } deselect() { this.selected = false; console.log('Deselect'); } } class MyClass { constructor() {} } // Estende MyClass para incluir o comportamento de Identifiable e Selectable interface MyClass extends Identifiable, Selectable {} // Função para aplicar mixins a uma classe function applyMixins(source: any, baseCtors: any[]) { baseCtors.forEach(baseCtor => { Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => { let descriptor = Object.getOwnPropertyDescriptor( baseCtor.prototype, name ); if (descriptor) { Object.defineProperty(source.prototype, name, descriptor); } }); }); } // Aplica os mixins a MyClass applyMixins(MyClass, [Identifiable, Selectable]); let o = new MyClass(); o.name = 'abc'; o.logId(); o.select(); ``` ### Recursos de Linguagem Assíncronos Como o TypeScript é um superconjunto do JavaScript, ele possui recursos de linguagem assíncronos integrados do JavaScript como: Promises: Promises são uma maneira de lidar com operações assíncronas e seus resultados usando métodos como `.then()` e `.catch()` para lidar com condições de sucesso e erro. Para saber mais: [https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Global_Objects/Promise](https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Global_Objects/Promise) Async/await: As palavras-chave async/await são uma maneira de fornecer uma sintaxe com aparência mais síncrona para trabalhar com Promises. A palavra-chave `async` é usada para definir uma função assíncrona, e a palavra-chave `await` é usada dentro de uma função async para pausar a execução até que uma Promise seja resolvida ou rejeitada. Para saber mais: [https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Statements/async_function](https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Statements/async_function) [https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Operators/await](https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Operators/await) As seguintes APIs são bem suportadas no TypeScript: Fetch API: [https://developer.mozilla.org/pt-BR/docs/Web/API/Fetch_API](https://developer.mozilla.org/pt-BR/docs/Web/API/Fetch_API) Web Workers: [https://developer.mozilla.org/pt-BR/docs/Web/API/Web_Workers_API](https://developer.mozilla.org/pt-BR/docs/Web/API/Web_Workers_API) Shared Workers: [https://developer.mozilla.org/pt-BR/docs/Web/API/SharedWorker](https://developer.mozilla.org/pt-BR/docs/Web/API/SharedWorker) WebSocket: [https://developer.mozilla.org/pt-BR/docs/Web/API/WebSockets_API](https://developer.mozilla.org/pt-BR/docs/Web/API/WebSockets_API) ### Iteradores e Geradores Tanto Iteradores quanto Geradores são bem suportados no TypeScript. Iteradores são objetos que implementam o protocolo iterador, fornecendo uma maneira de acessar elementos de uma coleção ou sequência um por um. É uma estrutura que contém um ponteiro para o próximo elemento na iteração. Eles possuem um método `next()` que retorna o próximo valor na sequência junto com um booleano indicando se a sequência terminou (`done`). ```typescript class NumberIterator implements Iterable { private current: number; constructor( private start: number, private end: number ) { this.current = start; } public next(): IteratorResult { if (this.current <= this.end) { const value = this.current; this.current++; return { value, done: false }; } else { return { value: undefined, done: true }; } } [Symbol.iterator](): Iterator { return this; } } const iterator = new NumberIterator(1, 3); for (const num of iterator) { console.log(num); } ``` Geradores são funções especiais definidas usando a sintaxe `function*` que simplifica a criação de iteradores. Eles usam a palavra-chave `yield` para definir a sequência de valores e pausar e retomar automaticamente a execução quando os valores são solicitados. Geradores facilitam a criação de iteradores e são especialmente úteis para trabalhar com sequências grandes ou infinitas. Exemplo: ```typescript function* numberGenerator(start: number, end: number): Generator { for (let i = start; i <= end; i++) { yield i; } } const generator = numberGenerator(1, 5); for (const num of generator) { console.log(num); } ``` O TypeScript também suporta iteradores assíncronos e geradores assíncronos. Para saber mais: [https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Global_Objects/Generator](https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Global_Objects/Generator) [https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Global_Objects/Iterator](https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Global_Objects/Iterator) ### Referência JSDoc TsDocs Ao trabalhar com uma base de código JavaScript, é possível ajudar o TypeScript a inferir o tipo correto usando comentários JSDoc com anotações adicionais para fornecer informações de tipo. Exemplo: ```typescript /** * Computa a potência de um dado número * @constructor * @param {number} base – O valor da base da expressão * @param {number} exponent – O valor do expoente da expressão */ function power(base: number, exponent: number) { return Math.pow(base, exponent); } power(10, 2); // function power(base: number, exponent: number): number ``` A documentação completa é fornecida neste link: [https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html](https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html) A partir da versão 3.7, é possível gerar definições de tipo .d.ts a partir da sintaxe JSDoc do JavaScript. Mais informações podem ser encontradas aqui: [https://www.typescriptlang.org/docs/handbook/declaration-files/dts-from-js.html](https://www.typescriptlang.org/docs/handbook/declaration-files/dts-from-js.html) ### @types Pacotes sob a organização @types são convenções especiais de nomenclatura de pacotes usadas para fornecer definições de tipo para bibliotecas ou módulos JavaScript existentes. Por exemplo, usando: ```shell npm install --save-dev @types/lodash ``` Instalará as definições de tipo de `lodash` em seu projeto atual. Para contribuir com as definições de tipo do pacote @types, envie um pull request para [https://github.com/DefinitelyTyped/DefinitelyTyped](https://github.com/DefinitelyTyped/DefinitelyTyped). ### JSX JSX (JavaScript XML) é uma extensão da sintaxe da linguagem JavaScript que permite escrever código semelhante a HTML em seus arquivos JavaScript ou TypeScript. É comumente usado no React para definir a estrutura HTML. O TypeScript estende as capacidades do JSX fornecendo verificação de tipos e análise estática. Para usar JSX, você precisa definir a opção de compilador `jsx` em seu arquivo `tsconfig.json`. Duas opções de configuração comuns: * "preserve": emite arquivos .jsx com o JSX inalterado. Esta opção diz ao TypeScript para manter a sintaxe JSX como está e não transformá-la durante o processo de compilação. Você pode usar esta opção se tiver uma ferramenta separada, como o Babel, que lida com a transformação. * "react": habilita a transformação JSX integrada do TypeScript. `React.createElement` será usado. Todas as opções estão disponíveis aqui: [https://www.typescriptlang.org/tsconfig#jsx](https://www.typescriptlang.org/tsconfig#jsx) ### Módulos ES6 O TypeScript suporta ES6 (ECMAScript 2015) e muitas versões subsequentes. Isso significa que você pode usar a sintaxe ES6, como arrow functions, template literals, classes, módulos, desestruturação e muito mais. Para habilitar recursos do ES6 em seu projeto, você pode especificar a propriedade `target` no tsconfig.json. Um exemplo de configuração: ```json { "compilerOptions": { "target": "es6", "module": "es6", "moduleResolution": "node", "sourceMap": true, "outDir": "dist" }, "include": ["src"] } ``` ### Operador de Exponenciação ES7 O operador de exponenciação (`**`) calcula o valor obtido elevando o primeiro operando à potência do segundo operando. Ele funciona de forma semelhante a `Math.pow()`, mas com a capacidade adicional de aceitar BigInts como operandos. O TypeScript suporta totalmente este operador usando como `target` no seu arquivo tsconfig.json a versão `es2016` ou superior. ```typescript console.log(2 ** (2 ** 2)); // 16 ``` ### A Instrução for-await-of Este é um recurso do JavaScript totalmente suportado no TypeScript que permite iterar sobre objetos iteráveis assíncronos a partir da versão de destino es2018. ```typescript async function* asyncNumbers(): AsyncIterableIterator { yield Promise.resolve(1); yield Promise.resolve(2); yield Promise.resolve(3); } (async () => { for await (const num of asyncNumbers()) { console.log(num); } })(); ``` ### Nova meta-propriedade target Você pode usar no TypeScript a meta-propriedade `new.target` que permite determinar se uma função ou construtor foi invocado usando o operador `new`. Ela permite detectar se um objeto foi criado como resultado de uma chamada de construtor. ```typescript class Parent { constructor() { console.log(new.target); // Registra a função construtora usada para criar uma instância } } class Child extends Parent { constructor() { super(); } } const parentX = new Parent(); // [Function: Parent] const child = new Child(); // [Function: Child] ``` ### Expressões de Importação Dinâmica É possível carregar módulos condicionalmente ou carregá-los sob demanda (lazy load) usando a proposta do ECMAScript para importação dinâmica, que é suportada no TypeScript. A sintaxe para expressões de importação dinâmica no TypeScript é as seguinte: ```typescript async function renderWidget() { const container = document.getElementById('widget'); if (container !== null) { const widget = await import('./widget'); // Importação dinâmica widget.render(container); } } renderWidget(); ``` ### "tsc –watch" Este comando inicia o compilador TypeScript com o parâmetro `--watch`, com a capacidade de recompilar automaticamente os arquivos TypeScript sempre que forem modificados. ```shell tsc --watch ``` A partir da versão 4.9 do TypeScript, o monitoramento de arquivos depende principalmente de eventos do sistema de arquivos, recorrendo automaticamente à sondagem (polling) se um observador baseado em eventos não puder ser estabelecido. ### Operador de Asserção Não-Nulo (Non-null Assertion Operator) O Operador de Asserção Não-Nulo (Postfix !) também chamado de Asserções de Atribuição Definitiva (Definite Assignment Assertions) é um recurso do TypeScript que permite asseverar que uma variável ou propriedade não é nula ou indefinida, mesmo que a análise de tipo estática do TypeScript sugira que poderia ser. Com este recurso, é possível remover qualquer verificação explícita. ```typescript type Person = { name: string; }; const printName = (person?: Person) => { console.log(`O nome é ${person!.name}`); }; ``` ### Declarações com Valor Padrão (Defaulted declarations) Declarações com valor padrão são usadas quando uma variável ou parâmetro recebe um valor padrão. Isso significa que se nenhum valor for fornecido para essa variável ou parâmetro, o valor padrão será usado no lugar. ```typescript function greet(name: string = 'Anônimo'): void { console.log(`Olá, ${name}!`); } greet(); // Olá, Anônimo! greet('John'); // Olá, John! ``` ### Encadeamento Opcional (Optional Chaining) O operador de encadeamento opcional `?.` funciona como o operador de ponto regular (`.`) para acessar propriedades ou métodos. No entanto, ele trata graciosamente valores nulos ou indefinidos terminando a expressão e retornando `undefined`, em vez de lançar um erro. ```typescript type Person = { name: string; age?: number; address?: { street?: string; city?: string; }; }; const person: Person = { name: 'John', }; console.log(person.address?.city); // undefined ``` ### Operador de Coalescência Nula (Nullish coalescing operator) O operador de coalescência nula `??` retorna o valor do lado direito se o lado esquerdo for `null` ou `undefined`; caso contrário, retorna o valor do lado esquerdo. ```typescript const foo = null ?? 'foo'; console.log(foo); // foo const baz = 1 ?? 'baz'; const baz2 = 0 ?? 'baz'; console.log(baz); // 1 console.log(baz2); // 0 ``` ### Tipos de Literal de Template (Template Literal Types) Tipos de Literal de Template permitem manipular valores de string em nível de tipo e gerar novos tipos de string baseados em existentes. Eles são úteis para criar tipos mais expressivos e precisos a partir de operações baseadas em string. ```typescript type Department = 'engineering' | 'hr'; type Language = 'english' | 'spanish'; type Id = `${Department}-${Language}-id`; // "engineering-english-id" | "engineering-spanish-id" | "hr-english-id" | "hr-spanish-id" ``` ### Sobrecarga de Função (Function overloading) A sobrecarga de função permite definir múltiplas assinaturas de função para o mesmo nome de função, cada uma com diferentes tipos de parâmetros e tipo de retorno. Quando você chama uma função sobrecarregada, o TypeScript usa os argumentos fornecidos para determinar a assinatura de função correta: ```typescript function makeGreeting(name: string): string; function makeGreeting(names: string[]): string[]; function makeGreeting(person: unknown): unknown { if (typeof person === 'string') { return `Hi ${person}!`; } else if (Array.isArray(person)) { return person.map(name => `Hi, ${name}!`); } throw new Error('Não foi possível saudar'); } makeGreeting('Simon'); makeGreeting(['Simone', 'John']); ``` ### Tipos Recursivos Um Tipo Recursivo é um tipo que pode se referir a si mesmo. Isso é útil para definir estruturas de dados que possuem uma estrutura hierárquica ou recursiva (aninhamento potencialmente infinito), como listas ligadas, árvores e grafos. ```typescript type ListNode = { data: T; next: ListNode | undefined; }; ``` ### Tipos Condicionais Recursivos É possível definir relacionamentos de tipo complexos usando lógica e recursão no TypeScript. Vamos detalhar em termos simples: Tipos Condicionais: permite definir tipos baseados em condições booleanas: ```typescript type CheckNumber = T extends number ? 'Número' : 'Não é um número'; type A = CheckNumber<123>; // 'Número' type B = CheckNumber<'abc'>; // 'Não é um número' ``` Recursão: significa uma definição de tipo que se refere a si mesma dentro de sua própria definição: ```typescript type Json = string | number | boolean | null | Json[] | { [key: string]: Json }; const data: Json = { prop1: true, prop2: 'prop2', prop3: { prop4: [], }, }; ``` Tipos Condicionais Recursivos combinam lógica condicional e recursão. Isso significa que uma definição de tipo pode depender de si mesma através de lógica condicional, criando relacionamentos de tipo complexos e flexíveis. ```typescript type Flatten = T extends Array ? Flatten : T; type NestedArray = [1, [2, [3, 4], 5], 6]; type FlattenedArray = Flatten; // 2 | 3 | 4 | 5 | 1 | 6 ``` ### Suporte a Módulos ECMAScript no Node O Node.js adicionou suporte para Módulos ECMAScript a partir da versão 15.3.0, e o TypeScript tem suporte a Módulos ECMAScript para Node.js desde a versão 4.7. Este suporte pode ser habilitado usando a propriedade `module` com o valor `nodenext` no arquivo tsconfig.json. Aqui está um exemplo: ```json { "compilerOptions": { "module": "nodenext", "outDir": "./lib", "declaration": true } } ``` O Node.js suporta duas extensões de arquivo para módulos: `.mjs` para módulos ES e `.cjs` para módulos CommonJS. As extensões de arquivo equivalentes no TypeScript são `.mts` para módulos ES e `.cts` para módulos CommonJS. Quando o compilador TypeScript transpila esses arquivos para JavaScript, ele criará arquivos `.mjs` e `.cjs`. Se você quiser usar módulos ES em seu projeto, você pode definir a propriedade `type` como "module" em seu arquivo package.json. Isso instrui o Node.js a tratar o projeto como um projeto de módulo ES. Além disso, o TypeScript também suporta declarações de tipo em arquivos .d.ts. Esses arquivos de declaração fornecem informações de tipo para bibliotecas ou módulos escritos em TypeScript, permitindo que outros desenvolvedores os utilizem com a verificação de tipo e os recursos de preenchimento automático do TypeScript. ### Funções de Asserção (Assertion Functions) No TypeScript, funções de asserção são funções que indicam a verificação de uma condição específica com base em seu valor de retorno. Em sua forma mais simples, uma função assert examina um predicado fornecido e lança um erro quando o predicado é avaliado como falso. ```typescript function isNumber(value: unknown): asserts value is number { if (typeof value !== 'number') { throw new Error('Não é um número'); } } ``` Ou pode ser declarada como uma expressão de função: ```typescript type AssertIsNumber = (value: unknown) => asserts value is number; const isNumber: AssertIsNumber = value => { if (typeof value !== 'number') { throw new Error('Não é um número'); } }; ``` Funções de asserção compartilham semelhanças com guardas de tipo (type guards). As guardas de tipo foram inicialmente introduzidas para realizar verificações em tempo de execução e garantir o tipo de um valor dentro de um escopo específico. Especificamente, uma guarda de tipo é uma função que avalia um predicado de tipo e retorna um valor booleano indicando se the predicado é verdadeiro ou falso. Isso difere ligeiramente das funções de asserção, onde a intenção é lançar um erro em vez de retornar falso quando o predicado não for satisfeito. Exemplo de guarda de tipo: ```typescript const isNumber = (value: unknown): value is number => typeof value === 'number'; ``` ### Tipos de Tupla Variádicos (Variadic Tuple Types) Tipos de Tupla Variádicos são recursos introduzidos na versão 4.0 do TypeScript. Vamos começar revisando o que é uma tupla: Um tipo tupla é um array que possui um comprimento definido, e onde o tipo de cada elemento é conhecido: ```typescript type Student = [string, number]; const [name, age]: Student = ['Simone', 20]; ``` O termo "variádico" significa aridade indefinida (aceita um número variável de argumentos). Uma tupla variádica é um tipo tupla que possui todas as propriedades anteriores, mas o formato exato ainda não está definido: ```typescript type Bar = [boolean, ...T, number]; type A = Bar<[boolean]>; // [boolean, boolean, number] type B = Bar<['a', 'b']>; // [boolean, 'a', 'b', number] type C = Bar<[]>; // [boolean, number] ``` No código anterior, podemos ver que o formato da tupla é definido pelo genérico `T` passado. Tuplas variádicas podem aceitar múltiplos genéricos, tornando-as muito flexíveis: ```typescript type Bar = [...T, boolean, ...G]; type A = Bar<[number], [string]>; // [number, boolean, string] type B = Bar<['a', 'b'], [boolean]>; // ["a", "b", boolean, boolean] ``` Com as novas tuplas variádicas podemos usar: * Os espalhamentos (spreads) na sintaxe de tipo tupla agora podem ser genéricos, assim podemos representar operações de ordem superior em tuplas e arrays mesmo quando não conhecemos os tipos reais sobre os quais estamos operando. * Os elementos de resto (rest elements) podem ocorrer em qualquer lugar em uma tupla. Exemplo: ```typescript type Items = readonly unknown[]; function concat( arr1: T, arr2: U ): [...T, ...U] { return [...arr1, ...arr2]; } concat([1, 2, 3], ['4', '5', '6']); // [1, 2, 3, "4", "5", "6"] ``` ### Tipos Boxed (Boxed types) Tipos boxed referem-se aos objetos de empacotamento que são usados para representar tipos primitivos como objetos. Esses objetos de empacotamento fornecem funcionalidades e métodos adicionais que não estão disponíveis diretamente nos valores primitivos. Quando você acessa um método como `charAt` ou `normalize` em um primitivo `string`, o JavaScript o empacota em um objeto `String`, chama o método e depois descarta o objeto. Demonstração: ```typescript const originalNormalize = String.prototype.normalize; String.prototype.normalize = function () { console.log(this, typeof this); return originalNormalize.call(this); }; console.log('\u0041'.normalize()); ``` O TypeScript representa essa diferenciação fornecendo tipos separados para os primitivos e seus empacotadores de objeto correspondentes: * string => String * number => Number * boolean => Boolean * symbol => Symbol * bigint => BigInt Os tipos boxed geralmente não são necessários. Evite usar tipos boxed e, em vez disso, use o tipo para os primitivos, por exemplo, `string` em vez de `String`. ### Covariância e Contravariância no TypeScript Covariância e Contravariância são usadas para descrever como os relacionamentos funcionam ao lidar com herança ou atribuição de tipos. Covariância significa que um relacionamento de tipo preserva a direção da herança ou atribuição, então se um tipo A é um subtipo do tipo B, então um array do tipo A também é considerado um subtipo de um array do tipo B. O importante a notar aqui é que o relacionamento de subtipo é mantido; isso significa que a Covariância aceita subtipo mas não aceita supertipo. Contravariância significa que um relacionamento de tipo inverte a direção da herança ou atribuição, então se um tipo A é um subtipo do tipo B, então um array do tipo B é considerado um subtipo de um array do tipo A. O relacionamento de subtipo é invertido; isso significa que a Contravariância aceita supertipo mas não aceita subtipo. Notas: Bivariância significa aceitar tanto supertipo quanto subtipo. Exemplo: Digamos que tenhamos um espaço para todos os animais e um espaço separado apenas para cachorros. Na Covariância, você pode colocar todos os cachorros no espaço dos animais porque cachorros são um tipo de animal. Mas você não pode colocar todos os animais no espaço dos cachorros porque pode haver outros animais misturados. Na Contravariância, você não pode colocar todos os animais no espaço dos cachorros porque o espaço dos animais também pode conter outros animais. No entanto, você pode colocar todos os cachorros no espaço dos animais porque todos os cachorros também são animais. ```typescript // Exemplo de Covariância class Animal { name: string; constructor(name: string) { this.name = name; } } class Dog extends Animal { breed: string; constructor(name: string, breed: string) { super(name); this.breed = breed; } } let animals: Animal[] = []; let dogs: Dog[] = []; // Covariância permite atribuir array de subtipo (Dog) a array de supertipo (Animal) animals = dogs; dogs = animals; // Inválido: Tipo 'Animal[]' não é atribuível ao tipo 'Dog[]' // Exemplo de Contravariância type Feed = (animal: T) => void; let feedAnimal: Feed = (animal: Animal) => { console.log(`Nome do animal: ${animal.name}`); }; let feedDog: Feed = (dog: Dog) => { console.log(`Nome do cachorro: ${dog.name}, Raça: ${dog.breed}`); }; // Contravariância permite atribuir callback de supertipo (Animal) a callback de subtipo (Dog) feedDog = feedAnimal; feedAnimal = feedDog; // Inválido: Tipo 'Feed' não é atribuível ao tipo 'Feed'. ``` No TypeScript, relacionamentos de tipo para arrays são covariantes, enquanto relacionamentos de tipo para parâmetros de função são contravariantes. Isso significa que o TypeScript exibe tanto covariância quanto contravariância, dependendo do contexto. #### Anotações de Variância Opcionais para Parâmetros de Tipo A partir do TypeScript 4.7.0, podemos usar as palavras-chave `out` e `in` para sermos específicos sobre a anotação de variância. Para Covariante, use a palavra-chave `out`: ```typescript type AnimalCallback = () => T; // T é Covariante aqui ``` E para Contravariante, use a palavra-chave `in`: ```typescript type AnimalCallback = (value: T) => void; // T é Contravariante aqui ``` ### Assinaturas de Índice de Padrão de String de Template (Template String Pattern Index Signatures) Assinaturas de índice de padrão de string de template permitem definir assinaturas de índice flexíveis usando padrões de string de template. Este recurso nos permite criar objetos que podem ser indexados com padrões específicos de chaves de string, fornecendo mais controle e especificidade ao acessar e manipular propriedades. O TypeScript a partir da versão 4.4 permite assinaturas de índice para símbolos e padrões de string de template. ```typescript const uniqueSymbol = Symbol('description'); type MyKeys = `key-${string}`; type MyObject = { [uniqueSymbol]: string; [key: MyKeys]: number; }; const obj: MyObject = { [uniqueSymbol]: 'Chave de símbolo único', 'key-a': 123, 'key-b': 456, }; console.log(obj[uniqueSymbol]); // Chave de símbolo único console.log(obj['key-a']); // 123 console.log(obj['key-b']); // 456 ``` ### O Operador satisfies O `satisfies` permite verificar se um determinado tipo satisfaz uma interface ou condição específica. Em outras palavras, ele garante que um tipo possui todas as propriedades e métodos exigidos de uma interface específica. É uma maneira de garantir que uma variável se encaixe na definição de um tipo. Aqui está um exemplo: ```typescript type Columns = 'name' | 'nickName' | 'attributes'; type User = Record; // Anotação de Tipo usando `User` const user: User = { name: 'Simone', nickName: undefined, attributes: ['dev', 'admin'], }; // Nas linhas seguintes, o TypeScript não conseguirá inferir corretamente user.attributes?.map(console.log); // A propriedade 'map' não existe no tipo 'string | string[]'. A propriedade 'map' não existe no tipo 'string'. user.nickName; // string | string[] | undefined // Asserção de tipo usando `as` const user2 = { name: 'Simon', nickName: undefined, attributes: ['dev', 'admin'], } as User; // Aqui também, o TypeScript não conseguirá inferir corretamente user2.attributes?.map(console.log); // A propriedade 'map' não existe no tipo 'string | string[]'. A propriedade 'map' não existe no tipo 'string'. user2.nickName; // string | string[] | undefined // Usando operadores `satisfies` podemos inferir os tipos corretamente agora const user3 = { name: 'Simon', nickName: undefined, attributes: ['dev', 'admin'], } satisfies User; user3.attributes?.map(console.log); // TypeScript infere corretamente: string[] user3.nickName; // TypeScript infere corretamente: undefined ``` ### Importações e Exportações Apenas de Tipo (Type-Only Imports and Export) Importações e Exportações Apenas de Tipo permitem importar ou exportar tipos sem importar ou exportar os valores ou funções associados a esses tipos. Isso pode ser útil para reduzir o tamanho do seu bundle. Para usar importações apenas de tipo, você pode usar a palavra-chave `import type`. O TypeScript permite usar extensões de arquivo de declaração e implementação (.ts, .mts, .cts e .tsx) em importações apenas de tipo, independentemente das configurações de `allowImportingTsExtensions`. Por exemplo: ```typescript import type { House } from './house.ts'; ``` As seguintes são formas suportadas: ```typescript import type T from './mod'; import type { A, B } from './mod'; import type * as Types from './mod'; export type { T }; export type { T } from './mod'; ``` ### declaração using e Gerenciamento Explícito de Recursos (Explicit Resource Management) Uma declaração `using` é um vínculo imutável com escopo de bloco, semelhante ao `const`, usado para gerenciar recursos descartáveis (disposable). Quando inicializado com um valor, o método `Symbol.dispose` desse valor é registrado e subsequentemente executado ao sair do escopo de bloco envolvente. Isso é baseado no recurso de Gerenciamento de Recursos do ECMAScript, que é útil para realizar tarefas essenciais de limpeza após a criação do objeto, como fechar conexões, excluir arquivos e liberar memória. Notas: * Devido à sua introdução recente na versão 5.2 do TypeScript, a maioria dos ambientes de execução carece de suporte nativo. Você precisará de polyfills para: `Symbol.dispose`, `Symbol.asyncDispose`, `DisposableStack`, `AsyncDisposableStack`, `SuppressedError`. * Além disso, você precisará configurar seu tsconfig.json da seguinte forma: ```json { "compilerOptions": { "target": "es2022", "lib": ["es2022", "esnext.disposable", "dom"] } } ``` Exemplo: ```typescript //@ts-ignore Symbol.dispose ??= Symbol('Symbol.dispose'); // Polyfill simples const doWork = (): Disposable => { return { [Symbol.dispose]: () => { console.log('disposto (disposed)'); }, }; }; console.log(1); { using work = doWork(); // Recurso é declarado console.log(2); } // Recurso é descartado (ex: `work[Symbol.dispose]()` é avaliado) console.log(3); ``` O código registrará: ```shell 1 2 disposto (disposed) 3 ``` Um recurso elegível para descarte deve aderir à interface `Disposable`: ```typescript // lib.esnext.disposable.d.ts interface Disposable { [Symbol.dispose](): void; } ``` As declarações `using` registram as operações de descarte de recursos em uma pilha, garantindo que sejam descartadas na ordem inversa da declaração: ```typescript { using j = getA(), y = getB(); using k = getC(); } // descarta `C`, depois `B`, depois `A`. ``` Os recursos têm garantia de serem descartados, mesmo que ocorra código subsequente ou exceções. Isso pode levar o descarte a potencialmente lançar uma exceção, possivelmente suprimindo outra. Para manter informações sobre erros suprimidos, uma nova exceção nativa, `SuppressedError`, é introduzida. #### declaração await using Uma declaração `await using` lida com um recurso descartável de forma assíncrona. O valor deve ter um método `Symbol.asyncDispose`, que será aguardado ao final do bloco. ```typescript async function doWorkAsync() { await using work = doWorkAsync(); // Recurso é declarado } // Recurso é descartado (ex: `await work[Symbol.asyncDispose]()` é avaliado) ``` Para um recurso descartável de forma assíncrona, ele deve aderir à interface `Disposable` ou `AsyncDisposable`: ```typescript // lib.esnext.disposable.d.ts interface AsyncDisposable { [Symbol.asyncDispose](): Promise; } ``` ```typescript //@ts-ignore Symbol.asyncDispose ??= Symbol('Symbol.asyncDispose'); // Polyfill simples class DatabaseConnection implements AsyncDisposable { // Um método que é chamado quando o objeto é descartado assincronamente [Symbol.asyncDispose]() { // Fecha a conexão e retorna uma promise return this.close(); } async close() { console.log('Fechando a conexão...'); await new Promise(resolve => setTimeout(resolve, 1000)); console.log('Conexão fechada.'); } } async function doWork() { // Cria uma nova conexão e descarte-a assincronamente quando ela sair do escopo await using connection = new DatabaseConnection(); // Recurso é declarado console.log('Fazendo algum trabalho...'); } // Recurso é descartado (ex: `await connection[Symbol.asyncDispose]()` é avaliado) doWork(); ``` O código registra: ```shell Fazendo algum trabalho... Fechando a conexão... Conexão fechada. ``` As declarações `using` e `await using` são permitidas em Instruções: `for`, `for-in`, `for-of`, `for-await-of`, `switch`. ### Atributos de Importação (Import Attributes) Os Atributos de Importação do TypeScript 5.3 (rótulos para importações) dizem ao ambiente de execução como lidar com módulos (JSON, etc.). Isso melhora a segurança garantindo importações claras e se alinha com a Política de Segurança de Conteúdo (CSP) para carregamento de recursos mais seguro. O TypeScript garante que eles sejam válidos, mas deixa o ambiente de execução lidar com sua interpretação para manipulação específica de módulos. Exemplo: ```typescript import config from './config.json' with { type: 'json' }; ``` com importação dinâmica: ```typescript const config = import('./config.json', { with: { type: 'json' } }); ``` ================================================ FILE: website/src/content/docs/pt-br/book/overloads.md ================================================ --- title: Sobrecargas (Overloads) sidebar: order: 51 label: 51. Sobrecargas (Overloads) --- As sobrecargas de função (function overloads) no TypeScript permitem definir múltiplas assinaturas para um mesmo nome de função, permitindo que as funções sejam chamadas de diversas maneiras. Aqui está um exemplo: ```typescript // Sobrecargas function sayHi(name: string): string; function sayHi(names: string[]): string[]; // Implementação function sayHi(name: unknown): unknown { if (typeof name === 'string') { return `Hi, ${name}!`; } else if (Array.isArray(name)) { return name.map(name => `Hi, ${name}!`); } throw new Error('Valor inválido'); } sayHi('xx'); // Válido sayHi(['aa', 'bb']); // Válido ``` Aqui está outro exemplo de uso de sobrecargas de função dentro de uma `class`: ```typescript class Greeter { message: string; constructor(message: string) { this.message = message; } // sobrecarga sayHi(name: string): string; sayHi(names: string[]): ReadonlyArray; // implementação sayHi(name: unknown): unknown { if (typeof name === 'string') { return `${this.message}, ${name}!`; } else if (Array.isArray(name)) { return name.map(name => `${this.message}, ${name}!`); } throw new Error('o valor é inválido'); } } console.log(new Greeter('Hello').sayHi('Simon')); ``` ================================================ FILE: website/src/content/docs/pt-br/book/predefined-conditional-types.md ================================================ --- title: Tipos Condicionais Predefinidos sidebar: order: 42 label: 42. Tipos Condicionais Predefinidos --- No TypeScript, os Tipos Condicionais Predefinidos são tipos condicionais integrados fornecidos pela linguagem. Eles são projetados para realizar transformações comuns de tipo com base nas características de um determinado tipo. `Exclude`: Este tipo remove todos os tipos de Type que são atribuíveis a ExcludedType. `Extract`: Este tipo extrai todos os tipos de Union que são atribuíveis a Type. `NonNullable`: Este tipo remove null e undefined de Type. `ReturnType`: Este tipo extrai o tipo de retorno de uma função Type. `Parameters`: Este tipo extrai os tipos de parâmetros de uma função Type. `Required`: Este tipo torna todas as propriedades em Type obrigatórias. `Partial`: Este tipo torna todas as propriedades em Type opcionais. `Readonly`: Este tipo torna todas as propriedades em Type somente leitura (readonly). ================================================ FILE: website/src/content/docs/pt-br/book/primitive-types.md ================================================ --- title: Tipos Primitivos sidebar: order: 10 label: 10. Tipos Primitivos --- O TypeScript suporta 7 tipos primitivos. Um tipo de dado primitivo refere-se a um tipo que não é um objeto e não possui nenhum método associado a ele. No TypeScript, todos os tipos primitivos são imutáveis, o que significa que seus valores não podem ser alterados uma vez que são atribuídos. ### string O tipo primitivo `string` armazena dados textuais, e o valor é sempre delimitado por aspas duplas ou simples. ```typescript const x: string = 'x'; const y: string = 'y'; ``` As strings podem abranger várias linhas se estiverem rodeadas pelo caractere de crase (`): ```typescript let sentence: string = `xxx, yyy`; ``` ### boolean O tipo de dado `boolean` no TypeScript armazena um valor binário, seja `true` ou `false`. ```typescript const isReady: boolean = true; ``` ### number Um tipo de dado `number` no TypeScript é representado com um valor de ponto flutuante de 64 bits. Um tipo `number` pode representar inteiros e frações. O TypeScript também suporta hexadecimal, binário e octal, por exemplo: ```typescript const decimal: number = 10; const hexadecimal: number = 0xa00d; // Hexadecimal começa com 0x const binary: number = 0b1010; // Binário começa com 0b const octal: number = 0o633; // Octal começa com 0o ``` ### bigInt Um `bigInt` representa valores numéricos muito grandes (253 – 1) e que não podem ser representados com um `number`. Um `bigInt` pode ser criado chamando a função integrada `BigInt()` ou adicionando `n` ao final de qualquer literal numérico inteiro: ```typescript const x: bigint = BigInt(9007199254740991); const y: bigint = 9007199254740991n; ``` Notas: * Valores `bigInt` não podem ser misturados com `number` e não podem ser usados com a função integrada `Math`; eles devem ser coagidos para o mesmo tipo. * Valores `bigInt` estão disponíveis apenas se a configuração da meta (target) for ES2020 ou superior. ### Symbol Symbols são identificadores únicos que podem ser usados como chaves de propriedade em objetos para evitar conflitos de nomenclatura. ```typescript type Obj = { [sym: symbol]: number; }; const a = Symbol('a'); const b = Symbol('b'); let obj: Obj = {}; obj[a] = 123; obj[b] = 456; console.log(obj[a]); // 123 console.log(obj[b]); // 456 ``` ### null e undefined Os tipos `null` e `undefined` representam a ausência de valor. O tipo `undefined` significa que o valor não foi atribuído ou inicializado, ou indica uma ausência não intencional de valor. O tipo `null` significa que sabemos que o campo não possui um valor, portanto o valor está indisponível; indica uma ausência intencional de valor. ### Array Um `array` é um tipo de dado que pode armazenar múltiplos valores do mesmo tipo ou não. Ele pode ser definido usando a seguinte sintaxe: ```typescript const x: string[] = ['a', 'b']; const y: Array = ['a', 'b']; const j: Array = ['a', 1, 'b', 2]; // União ``` O TypeScript suporta arrays somente leitura (readonly) usando a seguinte sintaxe: ```typescript const x: readonly string[] = ['a', 'b']; // Modificador readonly const y: ReadonlyArray = ['a', 'b']; const j: ReadonlyArray = ['a', 1, 'b', 2]; j.push('x'); // Inválido ``` O TypeScript suporta tupla e tupla somente leitura: ```typescript const x: [string, number] = ['a', 1]; const y: readonly [string, number] = ['a', 1]; ``` ### any O tipo de dado `any` representa literalmente "qualquer" valor; é o valor padrão quando o TypeScript não consegue inferir o tipo ou quando este não é especificado. Ao usar `any`, o compilador TypeScript ignora a verificação de tipo, portanto não há segurança de tipo quando o `any` está sendo usado. Geralmente, não use `any` para silenciar o compilador quando ocorre um erro; em vez disso, concentre-se em corrigir o erro, pois ao usar `any` é possível quebrar contratos e perdemos os benefícios do preenchimento automático do TypeScript. O tipo `any` pode ser útil durante uma migração gradual de JavaScript para TypeScript, pois pode silenciar o compilador. Para novos projetos, use a configuração do TypeScript `noImplicitAny`, que permite que o TypeScript emita erros onde `any` é usado ou inferido. O tipo `any` é geralmente uma fonte de erros que podem mascarar problemas reais com seus tipos. Evite usá-lo o máximo possível. ================================================ FILE: website/src/content/docs/pt-br/book/readonly-properties.md ================================================ --- title: Propriedades Somente Leitura (Readonly) sidebar: order: 13 label: 13. Propriedades Somente Leitura (Readonly) --- É possível impedir a escrita em uma propriedade usando o modificador `readonly`, que garante que a propriedade não possa ser reescrita, mas não fornece nenhuma garantia de imutabilidade total: ```typescript interface Y { readonly a: number; } type X = { readonly a: number; }; type J = Readonly<{ a: number; }>; type K = { readonly [index: number]: string; }; ``` ================================================ FILE: website/src/content/docs/pt-br/book/strictnullchecks.md ================================================ --- title: strictNullChecks sidebar: order: 18 label: 18. strictNullChecks --- `strictNullChecks` é uma opção do compilador TypeScript que impõe a verificação estrita de nulos. Quando esta opção está habilitada, variáveis e parâmetros só podem receber `null` | `undefined` se tiverem sido explicitamente declarados como sendo desse tipo usando o tipo de união `null` | `undefined`. Se uma variável ou parâmetro não for explicitamente declarado como anulável, o TypeScript gerará um erro para evitar possíveis erros de tempo de execução. ================================================ FILE: website/src/content/docs/pt-br/book/symbols.md ================================================ --- title: Símbolos (Symbols) sidebar: order: 58 label: 58. Símbolos (Symbols) --- Símbolos são um tipo de dado primitivo que representa um valor imutável que é garantido ser globalmente único durante todo o tempo de execução do programa. Símbolos podem ser usados como chaves para propriedades de objetos e fornecem uma maneira de criar propriedades não enumeráveis. ```typescript const key1: symbol = Symbol('key1'); const key2: symbol = Symbol('key2'); const obj = { [key1]: 'value 1', [key2]: 'value 2', }; console.log(obj[key1]); // value 1 console.log(obj[key2]); // value 2 ``` Em WeakMaps e WeakSets, símbolos agora são permitidos como chaves. ================================================ FILE: website/src/content/docs/pt-br/book/table-of-contents.md ================================================ --- title: Sumário sidebar: order: 4 label: 4. Sumário --- - O Livro Conciso de TypeScript - Traduções - Downloads e site - Sumário - Introdução - Sobre o Autor - Introdução ao TypeScript - O que é TypeScript? - Por que TypeScript? - TypeScript e JavaScript - Geração de Código TypeScript - JavaScript Moderno Agora (Downleveling) - Começando com TypeScript - Instalação - Configuração - Arquivo de Configuração do TypeScript - target - lib - strict - module - moduleResolution - esModuleInterop - jsx - skipLibCheck - files - include - exclude - importHelpers - Conselhos para Migração para TypeScript - Explorando o Sistema de Tipos - O Serviço de Linguagem do TypeScript - Tipagem Estrutural - Regras Fundamentais de Comparação do TypeScript - Tipos como Conjuntos - Atribuir um tipo: Declarações de Tipo e Asserções de Tipo - Declaração de Tipo - Asserção de Tipo - Declarações de Ambiente (Ambient Declarations) - Verificação de Propriedades e Verificação de Excesso de Propriedades - Tipos Fracos (Weak Types) - Verificação Estrita de Objeto Literal (Freshness) - Inferência de Tipo - Inferências Mais Avançadas - Alargamento de Tipo (Type Widening) - Const - Modificador Const em Parâmetros de Tipo - Asserção Const - Anotação de Tipo Explícita - Estreitamento de Tipo (Type Narrowing) - Condições - Lançando ou retornando - União Discriminada - Proteções de Tipo Definidas pelo Usuário (User-Defined Type Guards) - Tipos Primitivos - string - boolean - number - bigInt - Symbol - null e undefined - Array - any - Anotações de Tipo - Propriedades Opcionais - Propriedades Somente Leitura (Readonly) - Assinaturas de Índice (Index Signatures) - Estendendo Tipos - Tipos Literais - Inferência Literal - strictNullChecks - Enums - Enums numéricos - Enums de string - Enums constantes - Mapeamento reverso - Enums de ambiente - Membros computados e constantes - Estreitamento (Narrowing) - typeof type guards - Estreitamento de veracidade (Truthiness narrowing) - Estreitamento de igualdade (Equality narrowing) - Estreitamento com operador In - Estreitamento com instanceof - Atribuições - Análise de Fluxo de Controle - Predicados de Tipo - Uniões Discriminadas - O tipo never - Verificação de exaustividade - Tipos de Objeto - Tipo Tupla (Anônimo) - Tipo Tupla Nomeado (Rotulado) - Tupla de Comprimento Fixo - Tipo União - Tipos de Interseção - Indexação de Tipo - Tipo a partir de Valor - Tipo a partir de Retorno de Função - Tipo a partir de Módulo - Tipos Mapeados - Modificadores de Tipos Mapeados - Tipos Condicionais - Tipos Condicionais Distributivos - Inferência de tipo infer em Tipos Condicionais - Tipos Condicionais Predefinidos - Tipos de União de Template - Tipo Any - Tipo Unknown - Tipo Void - Tipo Never - Interface e Tipo - Sintaxe Comum - Tipos Básicos - Objetos e Interfaces - Tipos União e Interseção - Primitivos de Tipo Integrados - Objetos JS Integrados Comuns - Sobrecargas - Mesclagem e Extensão - Diferenças entre Type e Interface - Classe - Sintaxe Comum de Classe - Construtor - Construtores Privados e Protegidos - Modificadores de Acesso - Get e Set - Auto-Accessors em Classes - this - Propriedades de Parâmetro - Classes Abstratas - Com Genéricos - Decoradores - Decoradores de Classe - Decorador de Propriedade - Decorador de Método - Decoradores de Getter e Setter - Metadados de Decorador - Herança - Estáticos - Inicialização de propriedade - Sobrecarga de método - Genéricos - Tipo Genérico - Classes Genéricas - Restrições Genéricas - Estreitamento contextual genérico - Tipos Estruturais Apagados (Erased Structural Types) - Namespacing - Símbolos - Diretivas de Barra Tripla - Manipulação de Tipos - Criando Tipos a partir de Tipos - Tipos de Acesso Indexado - Tipos Utilitários - Awaited\ - Partial\ - Required\ - Readonly\ - Record\ - Pick\ - Omit\ - Exclude\ - Extract\ - NonNullable\ - Parameters\ - ConstructorParameters\ - ReturnType\ - InstanceType\ - ThisParameterType\ - OmitThisParameter\ - ThisType\ - Uppercase\ - Lowercase\ - Capitalize\ - Uncapitalize\ - NoInfer\ - Outros - Tratamento de Erros e Exceções - Classes Mixin - Recursos de Linguagem Assíncronos - Iteradores e Geradores - Referência JSDoc TsDocs - @types - JSX - Módulos ES6 - Operador de Exponenciação ES7 - A instrução for-await-of - Nova meta-propriedade target - Expressões de Importação Dinâmica - "tsc –watch" - Operador de Asserção Não-nula - Declarações padronizadas - Encadeamento Opcional (Optional Chaining) - Operador de coalescência nula - Tipos de Literais de Template - Sobrecarga de função - Tipos Recursivos - Tipos Condicionais Recursivos - Suporte a Módulo ECMAScript no Node - Funções de Asserção - Tipos de Tupla Variádicos - Tipos Boxed - Covariância e Contravariância no TypeScript - Anotações de Variância Opcionais para Parâmetros de Tipo - Assinaturas de Índice de Padrão de String de Template - O Operador satisfies - Importações e Exportações Apenas de Tipo - Declaração using e Gerenciamento Explícito de Recursos - Declaração await using - Atributos de Importação ================================================ FILE: website/src/content/docs/pt-br/book/template-union-types.md ================================================ --- title: Tipos de União de Template (Template Union Types) sidebar: order: 43 label: 43. Tipos de União de Template (Template Union Types) --- Tipos de união de template podem ser usados para mesclar e manipular texto dentro do sistema de tipos, por exemplo: ```typescript type Status = 'active' | 'inactive'; type Products = 'p1' | 'p2'; type ProductId = `id-${Products}-${Status}`; // "id-p1-active" | "id-p1-inactive" | "id-p2-active" | "id-p2-inactive" ``` ================================================ FILE: website/src/content/docs/pt-br/book/the-concise-typescript-book.md ================================================ --- title: O Livro Conciso de TypeScript sidebar: order: 1 label: 1. O Livro Conciso de TypeScript --- O Livro Conciso de TypeScript fornece uma visão geral abrangente e sucinta dos recursos do TypeScript. Ele oferece explicações claras que abrangem todos os aspectos encontrados na versão mais recente da linguagem, desde o seu poderoso sistema de tipos até recursos avançados. Seja você um iniciante ou um desenvolvedor experiente, este livro é um recurso inestimável para aprimorar sua compreensão e proficiência em TypeScript. Este livro é completamente Gratuito e de Código Aberto (Open Source). Acredito que a educação técnica de alta qualidade deve ser acessível a todos, por isso mantenho este livro gratuito e aberto. Se o livro te ajudou a resolver um bug, entender um conceito difícil ou avançar em sua carreira, considere apoiar meu trabalho pagando quanto quiser (preço sugerido: 15 USD) ou patrocinando um café. Seu apoio me ajuda a manter o conteúdo atualizado e a expandi-lo com novos exemplos e explicações mais profundas. [![Buy Me a Coffee](https://img.shields.io/badge/buy_me_a_coffee-FFDD00?style=for-the-badge&logo=buy-me-a-coffee&logoColor=black)](https://www.buymeacoffee.com/simonepoggiali) [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/donate/?business=QW82ZS956XLFY&no_recurring=0¤cy_code=EUR) ================================================ FILE: website/src/content/docs/pt-br/book/the-never-type.md ================================================ --- title: O tipo never sidebar: order: 25 label: 25. O tipo never --- Quando uma variável é estreitada para um tipo que não pode conter nenhum valor, o compilador TypeScript inferirá que a variável deve ser do tipo `never`. Isso ocorre porque o Tipo Never representa um valor que nunca pode ser produzido. ```typescript const printValue = (val: string | number) => { if (typeof val === 'string') { console.log(val.toUpperCase()); } else if (typeof val === 'number') { console.log(val.toFixed(2)); } else { // val tem o tipo never aqui porque nunca pode ser nada além de uma string ou um número const neverVal: never = val; console.log(`Valor inesperado: ${neverVal}`); } }; ``` ================================================ FILE: website/src/content/docs/pt-br/book/translations.md ================================================ --- title: Traduções sidebar: order: 2 label: 2. Traduções --- Este livro foi traduzido para várias versões de idiomas, incluindo: [Chinês](https://github.com/gibbok/typescript-book/blob/main/README-zh_CN.md) [Italiano](https://github.com/gibbok/typescript-book/blob/main/README-it_IT.md) [Português (Brasil)](https://github.com/gibbok/typescript-book/blob/main/README-pt_BR.md) [Sueco](https://github.com/gibbok/typescript-book/blob/main/README-sv_SE.md) ================================================ FILE: website/src/content/docs/pt-br/book/triple-slash-directives.md ================================================ --- title: Diretivas Triple-Slash sidebar: order: 59 label: 59. Diretivas Triple-Slash --- As diretivas triple-slash são comentários especiais que fornecem instruções ao compilador sobre como processar um arquivo. Essas diretivas começam com três barras consecutivas (`///`) e são normalmente colocadas no topo de um arquivo TypeScript e não têm efeitos no comportamento em tempo de execução. As diretivas triple-slash são usadas para referenciar dependências externas, especificar o comportamento de carregamento de módulos, habilitar/desabilitar certos recursos do compilador e muito mais. Alguns exemplos: Referenciando um arquivo de declaração: ```typescript /// ``` Indicar o formato do módulo: ```typescript /// ``` Habilitar opções do compilador, no exemplo a seguir, o modo estrito: ```typescript /// ``` ================================================ FILE: website/src/content/docs/pt-br/book/tuple-type-anonymous.md ================================================ --- title: Tipo Tupla (Anônimo) sidebar: order: 28 label: 28. Tipo Tupla (Anônimo) --- Um Tipo Tupla (Tuple Type) é um tipo que representa um array com um número fixo de elementos e seus tipos correspondentes. Um tipo tupla impõe um número específico de elementos e seus respectivos tipos em uma ordem fixa. Os tipos tupla são úteis quando você deseja representar uma coleção de valores com tipos específicos, onde a posição de cada elemento no array tem um significado específico. ```typescript type Point = [number, number]; ``` ================================================ FILE: website/src/content/docs/pt-br/book/type-annotations.md ================================================ --- title: Anotações de Tipo sidebar: order: 11 label: 11. Anotações de Tipo --- Em variáveis declaradas usando `var`, `let` e `const`, é possível adicionar opcionalmente um tipo: ```typescript const x: number = 1; ``` O TypeScript faz um bom trabalho ao inferir tipos, especialmente quando são simples, portanto essas declarações, na maioria dos casos, não são necessárias. Em funções, é possível adicionar anotações de tipo aos parâmetros: ```typescript function sum(a: number, b: number) { return a + b; } ``` O seguinte é um exemplo usando funções anônimas (as chamadas funções lambda): ```typescript const sum = (a: number, b: number) => a + b; ``` Essas anotações podem ser evitadas quando um valor padrão para um parâmetro está presente: ```typescript const sum = (a = 10, b: number) => a + b; ``` Anotações de tipo de retorno podem ser adicionadas às funções: ```typescript const sum = (a = 10, b: number): number => a + b; ``` Isso é útil especialmente para funções mais complexas, pois escrever explicitamente o tipo de retorno antes de uma implementação pode ajudar a pensar melhor sobre a função. Geralmente, considere anotar as assinaturas de tipo, mas não as variáveis locais do corpo, e sempre adicione tipos a objetos literais. ================================================ FILE: website/src/content/docs/pt-br/book/type-from-func-return.md ================================================ --- title: Tipo a partir de Retorno de Função sidebar: order: 35 label: 35. Tipo a partir de Retorno de Função --- Tipo a partir de Retorno de Função refere-se à capacidade de inferir automaticamente o tipo de retorno de uma função com base em sua implementação. Isso permite que o TypeScript determine o tipo do valor retornado pela função sem anotações de tipo explícitas. ```typescript const add = (x: number, y: number) => x + y; // O TypeScript pode inferir que o tipo de retorno da função é um número ``` ================================================ FILE: website/src/content/docs/pt-br/book/type-from-module.md ================================================ --- title: Tipo a partir de Módulo sidebar: order: 36 label: 36. Tipo a partir de Módulo --- Tipo a partir de Módulo refere-se à capacidade de usar os valores exportados de um módulo para inferir automaticamente seus tipos. Quando um módulo exporta um valor com um tipo específico, o TypeScript pode usar essa informação para inferir automaticamente o tipo desse valor quando ele é importado para outro módulo. ```typescript // calc.ts export const add = (x: number, y: number) => x + y; // index.ts import { add } from 'calc'; const r = add(1, 2); // r é number ``` ================================================ FILE: website/src/content/docs/pt-br/book/type-from-value.md ================================================ --- title: Tipo a partir de Valor sidebar: order: 34 label: 34. Tipo a partir de Valor --- Tipo a partir de Valor (Type from Value) no TypeScript refere-se à inferência automática de um tipo a partir de um valor ou expressão através da inferência de tipos. ```typescript const x = 'x'; // O TypeScript infere 'x' como um literal de string com 'const' (imutável), mas alarga para 'string' com 'let' (atribuível novamente). ``` ================================================ FILE: website/src/content/docs/pt-br/book/type-indexing.md ================================================ --- title: Indexação de Tipo sidebar: order: 33 label: 33. Indexação de Tipo --- Indexação de tipo (type indexing) refere-se à capacidade de definir tipos que podem ser indexados por uma chave não conhecida antecipadamente, usando uma assinatura de índice para especificar o tipo para propriedades que não são declaradas explicitamente. ```typescript type Dictionary = { [key: string]: T; }; const myDict: Dictionary = { a: 'a', b: 'b' }; console.log(myDict['a']); // Retorna a ``` ================================================ FILE: website/src/content/docs/pt-br/book/type-manipulation.md ================================================ --- title: Manipulação de Tipos sidebar: order: 60 label: 60. Manipulação de Tipos --- ### Criando Tipos a partir de Tipos É possível criar novos tipos compondo, manipulando ou transformando tipos existentes. Tipos Interseção (`&`): Permitem combinar múltiplos tipos em um único tipo: ```typescript type A = { foo: number }; type B = { bar: string }; type C = A & B; // Interseção de A e B const obj: C = { foo: 42, bar: 'hello' }; ``` Tipos União (`|`): Permitem definir um tipo que pode ser um de vários tipos: ```typescript type Result = string | number; const value1: Result = 'hello'; const value2: Result = 42; ``` Tipos Mapeados: Permitem transformar as propriedades de um tipo existente para criar um novo tipo: ```typescript type Mutable = { readonly [P in keyof T]: T[P]; }; type Person = { name: string; age: number; }; type ImmutablePerson = Mutable; // As propriedades tornam-se somente leitura ``` Tipos Condicionais: Permitem criar tipos com base em algumas condições: ```typescript type ExtractParam = T extends (param: infer P) => any ? P : never; type MyFunction = (name: string) => number; type ParamType = ExtractParam; // string ``` ### Tipos de Acesso Indexado (Indexed Access Types) No TypeScript, é possível acessar e manipular os tipos de propriedades dentro de outro tipo usando um índice, `Type[Key]`. ```typescript type Person = { name: string; age: number; }; type AgeType = Person['age']; // number ``` ```typescript type MyTuple = [string, number, boolean]; type MyType = MyTuple[2]; // boolean ``` ### Tipos Utilitários (Utility Types) Vários tipos utilitários integrados podem ser usados para manipular tipos, abaixo uma lista dos mais comuns: #### Awaited\ Constrói um tipo que descompacta recursivamente tipos Promise. ```typescript type A = Awaited>; // string ``` #### Partial\ Constrói um tipo com todas as propriedades de T definidas como opcionais. ```typescript type Person = { name: string; age: number; }; type A = Partial; // { name?: string | undefined; age?: number | undefined; } ``` #### Required\ Constrói um tipo com todas as propriedades de T definidas como obrigatórias. ```typescript type Person = { name?: string; age?: number; }; type A = Required; // { name: string; age: number; } ``` #### Readonly\ Constrói um tipo com todas as propriedades de T definidas como somente leitura. ```typescript type Person = { name: string; age: number; }; type A = Readonly; const a: A = { name: 'Simon', age: 17 }; a.name = 'John'; // Inválido ``` #### Record\ Constrói um tipo com um conjunto de propriedades K do tipo T. ```typescript type Product = { name: string; price: number; }; const products: Record = { apple: { name: 'Apple', price: 0.5 }, banana: { name: 'Banana', price: 0.25 }, }; console.log(products.apple); // { name: 'Apple', price: 0.5 } ``` #### Pick\ Constrói um tipo selecionando as propriedades especificadas K de T. ```typescript type Product = { name: string; price: number; }; type Price = Pick; // { price: number; } ``` #### Omit\ Constrói um tipo omitindo as propriedades especificadas K de T. ```typescript type Product = { name: string; price: number; }; type Name = Omit; // { name: string; } ``` #### Exclude\ Constrói um tipo excluindo todos os valores do tipo U de T. ```typescript type Union = 'a' | 'b' | 'c'; type MyType = Exclude; // b ``` #### Extract\ Constrói um tipo extraindo todos os valores do tipo U de T. ```typescript type Union = 'a' | 'b' | 'c'; type MyType = Extract; // a | c ``` #### NonNullable\ Constrói um tipo excluindo null e undefined de T. ```typescript type Union = 'a' | null | undefined | 'b'; type MyType = NonNullable; // 'a' | 'b' ``` #### Parameters\ Extrai os tipos de parâmetros de um tipo de função T. ```typescript type Func = (a: string, b: number) => void; type MyType = Parameters; // [a: string, b: number] ``` #### ConstructorParameters\ Extrai os tipos de parâmetros de um tipo de função construtora T. ```typescript class Person { constructor( public name: string, public age: number ) {} } type PersonConstructorParams = ConstructorParameters; // [name: string, age: number] const params: PersonConstructorParams = ['John', 30]; const person = new Person(...params); console.log(person); // Person { name: 'John', age: 30 } ``` #### ReturnType\ Extrai o tipo de retorno de um tipo de função T. ```typescript type Func = (name: string) => number; type MyType = ReturnType; // number ``` #### InstanceType\ Extrai o tipo de instância de um tipo de classe T. ```typescript class Person { name: string; constructor(name: string) { this.name = name; } sayHello() { console.log(`Olá, meu nome é ${this.name}!`); } } type PersonInstance = InstanceType; const person: PersonInstance = new Person('John'); person.sayHello(); // Olá, meu nome é John! ``` #### ThisParameterType\ Extrai o tipo do parâmetro 'this' de um tipo de função T. ```typescript interface Person { name: string; greet(this: Person): void; } type PersonThisType = ThisParameterType; // Person ``` #### OmitThisParameter\ Remove o parâmetro 'this' de um tipo de função T. ```typescript function capitalize(this: String) { return this[0].toUpperCase + this.substring(1).toLowerCase(); } type CapitalizeType = OmitThisParameter; // () => string ``` #### ThisType\ Serve como um marcador para um tipo `this` contextual. ```typescript type Logger = { log: (error: string) => void; }; let helperFunctions: { [name: string]: Function } & ThisType = { hello: function () { this.log('some error'); // Válido, pois "log" faz parte de "this". this.update(); // Inválido }, }; ``` #### Uppercase\ Converte para maiúsculas o nome do tipo de entrada T. ```typescript type MyType = Uppercase<'abc'>; // "ABC" ``` #### Lowercase\ Converte para minúsculas o nome do tipo de entrada T. ```typescript type MyType = Lowercase<'ABC'>; // "abc" ``` #### Capitalize\ Coloca a primeira letra em maiúscula no nome do tipo de entrada T. ```typescript type MyType = Capitalize<'abc'>; // "Abc" ``` #### Uncapitalize\ Coloca a primeira letra em minúscula no nome do tipo de entrada T. ```typescript type MyType = Uncapitalize<'Abc'>; // "abc" ``` #### NoInfer\ `NoInfer` é um tipo utilitário projetado para bloquear a inferência automática de tipos dentro do escopo de uma função genérica. Exemplo: ```typescript // Inferência automática de tipos dentro do escopo de uma função genérica. function fn(x: T[], y: T) { return x.concat(y); } const r = fn(['a', 'b'], 'c'); // O tipo aqui é ("a" | "b" | "c")[] ``` Com NoInfer: ```typescript // Exemplo de função que usa NoInfer para evitar inferência de tipo function fn2(x: T[], y: NoInfer) { return x.concat(y); } const r2 = fn2(['a', 'b'], 'c'); // Erro: Argumento de tipo '"c"' não é atribuível ao parâmetro do tipo '"a" | "b"'. ``` ================================================ FILE: website/src/content/docs/pt-br/book/type-predicates.md ================================================ --- title: Predicados de Tipo sidebar: order: 23 label: 23. Predicados de Tipo --- Predicados de Tipo (Type Predicates) no TypeScript são funções que retornam um valor booleano e são usadas para estreitar o tipo de uma variável para um tipo mais específico. ```typescript const isString = (value: unknown): value is string => typeof value === 'string'; const foo = (bar: unknown) => { if (isString(bar)) { console.log(bar.toUpperCase()); } else { console.log('não é uma string'); } }; ``` ================================================ FILE: website/src/content/docs/pt-br/book/typescript-introduction.md ================================================ --- title: Introdução ao TypeScript sidebar: order: 7 label: 7. Introdução ao TypeScript --- ### O que é TypeScript? TypeScript é uma linguagem de programação fortemente tipada que se baseia no JavaScript. Foi originalmente projetada por Anders Hejlsberg em 2012 e é atualmente desenvolvida e mantida pela Microsoft como um projeto de código aberto. O TypeScript compila para JavaScript e pode ser executado em qualquer ambiente de execução JavaScript (por exemplo, um navegador ou Node.js em um servidor). Ele suporta múltiplos paradigmas de programação, como funcional, genérica, imperativa e orientada a objetos, e é uma linguagem compilada (transpilada) que é convertida em JavaScript antes da execução. ### Por que TypeScript? TypeScript é uma linguagem fortemente tipada que ajuda a prevenir erros comuns de programação e a evitar certos tipos de erros em tempo de execução antes que o programa seja executado. Uma linguagem fortemente tipada permite ao desenvolvedor especificar várias restrições e comportamentos do programa nas definições de tipos de dados, facilitando a capacidade de verificar a correção do software e prevenir defeitos. Isso é especialmente valioso em aplicações de larga escala. Alguns dos benefícios do TypeScript: * Tipagem estática, opcionalmente fortemente tipada * Inferência de Tipo * Acesso a recursos ES6 e ES7 * Compatibilidade multiplataforma e entre navegadores * Suporte de ferramentas com IntelliSense ### TypeScript e JavaScript Arquivos TypeScript são escritos em arquivos `.ts` ou `.tsx`, enquanto arquivos JavaScript são escritos em `.js` ou `.jsx`. Arquivos com a extensão `.tsx` ou `.jsx` podem conter a Extensão de Sintaxe JavaScript JSX, que é usada no React para desenvolvimento de UI. O TypeScript é um superconjunto tipado de JavaScript (ECMAScript 2015) em termos de sintaxe. Todo código JavaScript é código TypeScript válido, mas o inverso nem sempre é verdadeiro. Por exemplo, considere uma função em um arquivo JavaScript com a extensão `.js`, como a seguinte: ```typescript const sum = (a, b) => a + b; ``` A função pode ser convertida e usada no TypeScript alterando a extensão do arquivo para `.ts`. No entanto, se a mesma função for anotada com tipos TypeScript, ela não poderá ser executada em nenhum ambiente de execução JavaScript sem compilação. O seguinte código TypeScript produzirá um erro de sintaxe se não for compilado: ```typescript const sum = (a: number, b: number): number => a + b; ``` O TypeScript foi projetado para detectar possíveis exceções que podem ocorrer em tempo de execução durante o tempo de compilação, fazendo com que o desenvolvedor defina a intenção com anotações de tipo. Além disso, o TypeScript também pode capturar problemas se nenhuma anotação de tipo for fornecida. Por exemplo, o seguinte trecho de código não especifica nenhum tipo TypeScript: ```typescript const items = [{ x: 1 }, { x: 2 }]; const result = items.filter(item => item.y); ``` Neste caso, o TypeScript detecta um erro e informa: ```text Property 'y' does not exist on type '{ x: number; }'. ``` O sistema de tipos do TypeScript é amplamente influenciado pelo comportamento de tempo de execução do JavaScript. Por exemplo, o operador de adição (+), que no JavaScript pode realizar a concatenação de strings ou a adição numérica, é modelado da mesma forma no TypeScript: ```typescript const result = '1' + 1; // Result is of type string ``` A equipe por trás do TypeScript tomou a decisão deliberada de sinalizar o uso incomum do JavaScript como erros. Por exemplo, considere o seguinte código JavaScript válido: ```typescript const result = 1 + true; // In JavaScript, the result is equal 2 ``` No entanto, o TypeScript lança um erro: ```text Operator '+' cannot be applied to types 'number' and 'boolean'. ``` Este erro ocorre porque o TypeScript impõe estritamente a compatibilidade de tipos e, neste caso, identifica uma operação inválida entre um número e um booleano. ### Geração de Código TypeScript O compilador TypeScript tem duas responsabilidades principais: verificar se há erros de tipo e compilar para JavaScript. Esses dois processos são independentes um do outro. Os tipos não afetam a execução do código em um ambiente de execução JavaScript, pois são completamente apagados durante a compilação. O TypeScript ainda pode gerar JavaScript mesmo na presença de erros de tipo. Aqui está um exemplo de código TypeScript com um erro de tipo: ```typescript const add = (a: number, b: number): number => a + b; const result = add('x', 'y'); // Argument of type 'string' is not assignable to parameter of type 'number'. ``` No entanto, ele ainda pode produzir uma saída JavaScript executável: ```typescript 'use strict'; const add = (a, b) => a + b; const result = add('x', 'y'); // xy ``` Não é possível verificar tipos TypeScript em tempo de execução. Por exemplo: ```typescript interface Animal { name: string; } interface Dog extends Animal { bark: () => void; } interface Cat extends Animal { meow: () => void; } const makeNoise = (animal: Animal) => { if (animal instanceof Dog) { // 'Dog' only refers to a type, but is being used as a value here. // ... } }; ``` Como os tipos são apagados após a compilação, não há como executar este código em JavaScript. Para reconhecer tipos em tempo de execução, precisamos usar outro mecanismo. O TypeScript fornece várias opções, sendo uma comum a "união tagueada" (tagged union). Por exemplo: ```typescript interface Dog { kind: 'dog'; // Tagged union bark: () => void; } interface Cat { kind: 'cat'; // Tagged union meow: () => void; } type Animal = Dog | Cat; const makeNoise = (animal: Animal) => { if (animal.kind === 'dog') { animal.bark(); } else { animal.meow(); } }; const dog: Dog = { kind: 'dog', bark: () => console.log('bark'), }; makeNoise(dog); ``` A propriedade "kind" é um valor que pode ser usado em tempo de execução para distinguir entre objetos em JavaScript. Também é possível que um valor em tempo de execução tenha um tipo diferente daquele declarado na declaração de tipo. Por exemplo, se o desenvolvedor interpretou mal um tipo de API e o anotou incorretamente. O TypeScript é um superconjunto do JavaScript, portanto a palavra-chave "class" pode ser usada como um tipo e valor em tempo de execução. ```typescript class Animal { constructor(public name: string) {} } class Dog extends Animal { constructor( public name: string, public bark: () => void ) { super(name); } } class Cat extends Animal { constructor( public name: string, public meow: () => void ) { super(name); } } type Mammal = Dog | Cat; const makeNoise = (mammal: Mammal) => { if (mammal instanceof Dog) { mammal.bark(); } else { mammal.meow(); } }; const dog = new Dog('Fido', () => console.log('bark')); makeNoise(dog); ``` No JavaScript, uma "classe" tem uma propriedade "prototype", e o operador "instanceof" pode ser usado para testar se a propriedade prototype de um construtor aparece em qualquer lugar na cadeia de protótipos de um objeto. O TypeScript não tem efeito no desempenho em tempo de execução, pois todos os tipos serão apagados. No entanto, o TypeScript introduz alguma sobrecarga no tempo de compilação. ### JavaScript Moderno Agora (Downleveling) O TypeScript pode compilar código para qualquer versão lançada do JavaScript desde o ECMAScript 3 (1999). Isso significa que o TypeScript pode transpilar o código dos recursos JavaScript mais recentes para versões mais antigas, um processo conhecido como *Downleveling*. Isso permite o uso do JavaScript moderno, mantendo a compatibilidade máxima com ambientes de execução mais antigos. É importante notar que durante a transpilação para uma versão mais antiga do JavaScript, o TypeScript pode gerar código que pode incorrer em uma sobrecarga de desempenho em comparação com as implementações nativas. Aqui estão alguns dos recursos modernos do JavaScript que podem ser usados no TypeScript: * Módulos ECMAScript em vez de callbacks "define" no estilo AMD ou instruções "require" do CommonJS. * Classes em vez de protótipos. * Declaração de variáveis usando "let" ou "const" em vez de "var". * Loop "for-of" ou ".forEach" em vez do loop "for" tradicional. * Funções de seta (Arrow functions) em vez de expressões de função. * Atribuição via desestruturação (Destructuring assignment). * Nomes de propriedade/método abreviados e nomes de propriedade computados. * Parâmetros de função padrão. Ao aproveitar esses recursos modernos do JavaScript, os desenvolvedores podem escrever códigos mais expressivos e concisos no TypeScript. ================================================ FILE: website/src/content/docs/pt-br/book/union-type.md ================================================ --- title: Tipo União sidebar: order: 31 label: 31. Tipo União --- Um Tipo União (Union Type) é um tipo que representa um valor que pode ser um de vários tipos. Tipos União são denotados usando o símbolo `|` entre cada tipo possível. ```typescript let x: string | number; x = 'hello'; // Válido x = 123; // Válido ``` ================================================ FILE: website/src/content/docs/pt-br/book/unknown-type.md ================================================ --- title: Tipo unknown sidebar: order: 45 label: 45. Tipo unknown --- No TypeScript, o tipo `unknown` representa um valor que é de um tipo desconhecido. Ao contrário do tipo `any`, que permite qualquer tipo de valor, o `unknown` exige uma verificação de tipo ou asserção antes de poder ser usado de uma maneira específica, portanto nenhuma operação é permitida em um `unknown` sem primeiro asseverar ou estreitar para um tipo mais específico. O tipo `unknown` só é atribuível a si mesmo e ao tipo `any`; é uma alternativa segura em termos de tipos ao `any`. ```typescript let value: unknown; let value1: unknown = value; // Válido let value2: any = value; // Válido let value3: boolean = value; // Inválido let value4: number = value; // Inválido ``` ```typescript const add = (a: unknown, b: unknown): number | undefined => typeof a === 'number' && typeof b === 'number' ? a + b : undefined; console.log(add(1, 2)); // 3 console.log(add('x', 2)); // undefined ``` ================================================ FILE: website/src/content/docs/pt-br/book/void-type.md ================================================ --- title: Tipo void sidebar: order: 46 label: 46. Tipo void --- O tipo `void` é usado para indicar que uma função não retorna um valor. ```typescript const sayHello = (): void => { console.log('Hello!'); }; ``` ================================================ FILE: website/src/content/docs/pt-br/index.mdx ================================================ --- title: TypeScript Book description: The Concise TypeScript Book template: splash hero: tagline: O Concise TypeScript Book fornece uma visão geral abrangente e sucinta das capacidades do TypeScript. Ele oferece explicações claras cobrindo todos os aspectos encontrados na versão mais recente da linguagem, desde seu poderoso sistema de tipos até recursos avançados. Seja você um iniciante ou um desenvolvedor experiente, este livro é um recurso inestimável para aprimorar sua compreensão e proficiência em TypeScript.

Este livro é completamente Gratuito e de Código Aberto. actions: - text: Leia agora! link: /typescript-book/pt-br/book/the-concise-typescript-book/ icon: right-arrow variant: primary - text: GitHub link: https://github.com/gibbok/typescript-book icon: github variant: secondary - text: X.com link: https://twitter.com/gibbok_coding icon: x.com variant: secondary --- ================================================ FILE: website/src/content/docs/sv-se/book/about-the-author.md ================================================ --- title: Om författaren sidebar: order: 6 label: 6. Om författaren --- Simone Poggiali är en erfaren Staff Engineer med en passion för att skriva professionell kod sedan 90-talet. Under sin internationella karriär har han bidragit till många projekt för ett brett spektrum av kunder, från startups till stora organisationer. Framstående företag som HelloFresh, Siemens, O2, Leroy Merlin och Snowplow har dragit nytta av hans expertis och engagemang. Du kan nå Simone Poggiali på följande plattformar: * LinkedIn: [https://www.linkedin.com/in/simone-poggiali](https://www.linkedin.com/in/simone-poggiali) * GitHub: [https://github.com/gibbok](https://github.com/gibbok) * X.com: [https://x.com/gibbok_coding](https://x.com/gibbok_coding) * E-post: gibbok.coding📧gmail.com Fullständig lista över bidragsgivare: [https://github.com/gibbok/typescript-book/graphs/contributors](https://github.com/gibbok/typescript-book/graphs/contributors) ================================================ FILE: website/src/content/docs/sv-se/book/any-type.md ================================================ --- title: Any-typen sidebar: order: 44 label: 44. Any-typen --- `any`-typen är en speciell typ (universell supertyp) som kan användas för att representera vilken typ av värde som helst (primitiver, objekt, arrayer, funktioner, fel, symboler). Den används ofta i situationer där typen av ett värde inte är känd vid kompilering, eller vid arbete med värden från externa API:er eller bibliotek som inte har TypeScript-typningar. Genom att använda `any`-typen indikerar du för TypeScript-kompilatorn att värden ska representeras utan några begränsningar. För att maximera typsäkerheten i din kod, överväg följande: * Begränsa användningen av `any` till specifika fall där typen verkligen är okänd. * Returnera inte `any`-typer från en funktion, eftersom detta försvagar typsäkerheten i kod som använder den. * Istället för `any`, använd `@ts-ignore` om du behöver tysta kompilatorn. ```typescript let value: any; value = true; // Valid value = 7; // Valid ``` ================================================ FILE: website/src/content/docs/sv-se/book/assignments.md ================================================ --- title: Tilldelningar sidebar: order: 21 label: 21. Tilldelningar --- TypeScript-avsmalning med hjälp av tilldelningar är ett sätt att avsmalma typen av en variabel baserat på det tilldelade värdet. När en variabel tilldelas ett värde härleder TypeScript dess typ baserat på det tilldelade värdet, och avsmalmar variabelns typ för att matcha den härledda typen. ```typescript let value: string | number; value = 'hello'; if (typeof value === 'string') { console.log(value.toUpperCase()); } value = 42; if (typeof value === 'number') { console.log(value.toFixed(2)); } ``` ================================================ FILE: website/src/content/docs/sv-se/book/built-in-type-primitives.md ================================================ --- title: Inbyggda primitiva typer sidebar: order: 49 label: 49. Inbyggda primitiva typer --- TypeScript har flera inbyggda primitiva typer som kan användas för att definiera variabler, funktionsparametrar och returtyper: * `number`: Representerar numeriska värden, inklusive heltal och flyttal. * `string`: Representerar textdata. * `boolean`: Representerar logiska värden, som kan vara antingen true eller false. * `null`: Representerar frånvaron av ett värde. * `undefined`: Representerar ett värde som inte har tilldelats eller inte har definierats. * `symbol`: Representerar en unik identifierare. Symboler används vanligtvis som nycklar för objektegenskaper. * `bigint`: Representerar heltal med godtycklig precision. * `any`: Representerar en dynamisk eller okänd typ. Variabler av typen any kan innehålla värden av vilken typ som helst, och de kringgår typkontroll. * `void`: Representerar frånvaron av någon typ. Den används vanligtvis som returtyp för funktioner som inte returnerar ett värde. * `never`: Representerar en typ för värden som aldrig förekommer. Den används vanligtvis som returtyp för funktioner som kastar ett fel eller går in i en oändlig loop. ================================================ FILE: website/src/content/docs/sv-se/book/class.md ================================================ --- title: Klass sidebar: order: 54 label: 54. Klass --- ### Vanlig klasssyntax Nyckelordet `class` används i TypeScript för att definiera en klass. Nedan kan du se ett exempel: ```typescript class Person { private name: string; private age: number; constructor(name: string, age: number) { this.name = name; this.age = age; } public sayHi(): void { console.log( `Hello, my name is ${this.name} and I am ${this.age} years old.` ); } } ``` Nyckelordet `class` används för att definiera en klass som heter "Person". Klassen har två privata egenskaper: name av typen `string` och age av typen `number`. Konstruktorn definieras med nyckelordet `constructor`. Den tar name och age som parametrar och tilldelar dem till motsvarande egenskaper. Klassen har en `public` metod som heter sayHi som loggar ett hälsningsmeddelande. För att skapa en instans av en klass i TypeScript kan du använda nyckelordet `new` följt av klassnamnet, följt av parenteser `()`. Till exempel: ```typescript const myObject = new Person('John Doe', 25); myObject.sayHi(); // Output: Hello, my name is John Doe and I am 25 years old. ``` ### Konstruktor Konstruktorer är speciella metoder inom en klass som används för att initiera objektets egenskaper när en instans av klassen skapas. ```typescript class Person { public name: string; public age: number; constructor(name: string, age: number) { this.name = name; this.age = age; } sayHello() { console.log( `Hello, my name is ${this.name} and I'm ${this.age} years old.` ); } } const john = new Person('Simon', 17); john.sayHello(); ``` Det är möjligt att överlagra en konstruktor med följande syntax: ```typescript type Sex = 'm' | 'f'; class Person { name: string; age: number; sex: Sex; constructor(name: string, age: number, sex?: Sex); constructor(name: string, age: number, sex: Sex) { this.name = name; this.age = age; this.sex = sex ?? 'm'; } } const p1 = new Person('Simon', 17); const p2 = new Person('Alice', 22, 'f'); ``` I TypeScript är det möjligt att definiera flera konstruktoröverlagringar, men du kan bara ha en implementering som måste vara kompatibel med alla överlagringar. Detta kan uppnås genom att använda en valfri parameter. ```typescript class Person { name: string; age: number; constructor(); constructor(name: string); constructor(name: string, age: number); constructor(name?: string, age?: number) { this.name = name ?? 'Unknown'; this.age = age ?? 0; } displayInfo() { console.log(`Name: ${this.name}, Age: ${this.age}`); } } const person1 = new Person(); person1.displayInfo(); // Name: Unknown, Age: 0 const person2 = new Person('John'); person2.displayInfo(); // Name: John, Age: 0 const person3 = new Person('Jane', 25); person3.displayInfo(); // Name: Jane, Age: 25 ``` ### Privata och skyddade konstruktorer I TypeScript kan konstruktorer markeras som privata eller skyddade, vilket begränsar deras åtkomlighet och användning. Privata konstruktorer: Kan bara anropas inom själva klassen. Privata konstruktorer används ofta i scenarier där du vill upprätthålla ett singleton-mönster eller begränsa skapandet av instanser till en fabriksmetod inom klassen. Skyddade konstruktorer: Skyddade konstruktorer är användbara när du vill skapa en basklass som inte ska instansieras direkt men kan utökas av underklasser. ```typescript class BaseClass { protected constructor() {} } class DerivedClass extends BaseClass { private value: number; constructor(value: number) { super(); this.value = value; } } // Attempting to instantiate the base class directly will result in an error // const baseObj = new BaseClass(); // Error: Constructor of class 'BaseClass' is protected. // Create an instance of the derived class const derivedObj = new DerivedClass(10); ``` ### Åtkomstmodifierare Åtkomstmodifierarna `private`, `protected` och `public` används för att styra synligheten och åtkomsten till klassmedlemmar, såsom egenskaper och metoder, i TypeScript-klasser. Dessa modifierare är viktiga för att upprätthålla inkapsling och för att etablera gränser för åtkomst och modifiering av en klass interna tillstånd. Modifieraren `private` begränsar åtkomsten till klassmedlemmen enbart inom den innehållande klassen. Modifieraren `protected` tillåter åtkomst till klassmedlemmen inom den innehållande klassen och dess härledda klasser. Modifieraren `public` ger obegränsad åtkomst till klassmedlemmen och tillåter att den nås från var som helst. ### Get och Set Getters och setters är speciella metoder som låter dig definiera anpassat åtkomst- och ändringsbeteende för klassegenskaper. De gör det möjligt att kapsla in det interna tillståndet hos ett objekt och tillhandahålla ytterligare logik vid hämtning eller inställning av egenskapsvärden. I TypeScript definieras getters och setters med nyckelorden `get` respektive `set`. Här är ett exempel: ```typescript class MyClass { private _myProperty: string; constructor(value: string) { this._myProperty = value; } get myProperty(): string { return this._myProperty; } set myProperty(value: string) { this._myProperty = value; } } ``` ### Auto-accessorer i klasser TypeScript version 4.9 lägger till stöd för auto-accessorer, en kommande ECMAScript-funktion. De liknar klassegenskaper men deklareras med nyckelordet "accessor". ```typescript class Animal { accessor name: string; constructor(name: string) { this.name = name; } } ``` Auto-accessorer "avsockras" till privata `get`- och `set`-accessorer som opererar på en otillgänglig egenskap. ```typescript class Animal { #__name: string; get name() { return this.#__name; } set name(value: string) { this.#__name = name; } constructor(name: string) { this.name = name; } } ``` ### this I TypeScript refererar nyckelordet `this` till den aktuella instansen av en klass inom dess metoder eller konstruktorer. Det ger dig möjlighet att komma åt och modifiera klassens egenskaper och metoder inifrån dess eget scope. Det erbjuder ett sätt att komma åt och manipulera det interna tillståndet hos ett objekt inom dess egna metoder. ```typescript class Person { private name: string; constructor(name: string) { this.name = name; } public introduce(): void { console.log(`Hello, my name is ${this.name}.`); } } const person1 = new Person('Alice'); person1.introduce(); // Hello, my name is Alice. ``` ### Parameteregenskaper Parameteregenskaper gör det möjligt att deklarera och initiera klassegenskaper direkt i konstruktorns parametrar, vilket undviker överflödig kod. Exempel: ```typescript class Person { constructor( private name: string, public age: number ) { // The "private" and "public" keywords in the constructor // automatically declare and initialize the corresponding class properties. } public introduce(): void { console.log( `Hello, my name is ${this.name} and I am ${this.age} years old.` ); } } const person = new Person('Alice', 25); person.introduce(); ``` ### Abstrakta klasser Abstrakta klasser används i TypeScript främst för arv; de erbjuder ett sätt att definiera gemensamma egenskaper och metoder som kan ärvas av underklasser. Detta är användbart när du vill definiera gemensamt beteende och säkerställa att underklasser implementerar vissa metoder. De ger ett sätt att skapa en hierarki av klasser där den abstrakta basklassen tillhandahåller ett delat gränssnitt och gemensam funktionalitet för underklasserna. ```typescript abstract class Animal { protected name: string; constructor(name: string) { this.name = name; } abstract makeSound(): void; } class Cat extends Animal { makeSound(): void { console.log(`${this.name} meows.`); } } const cat = new Cat('Whiskers'); cat.makeSound(); // Output: Whiskers meows. ``` ### Med generics Klasser med generics gör det möjligt att definiera återanvändbara klasser som kan arbeta med olika typer. ```typescript class Container { private item: T; constructor(item: T) { this.item = item; } getItem(): T { return this.item; } setItem(item: T): void { this.item = item; } } const container1 = new Container(42); console.log(container1.getItem()); // 42 const container2 = new Container('Hello'); container2.setItem('World'); console.log(container2.getItem()); // World ``` ### Dekoratörer Dekoratörer tillhandahåller en mekanism för att lägga till metadata, ändra beteende, validera eller utöka funktionaliteten hos målelementet. De är funktioner som körs vid körning. Flera dekoratörer kan tillämpas på en deklaration. Dekoratörer är experimentella funktioner, och följande exempel är bara kompatibla med TypeScript version 5 eller senare med ES6. För TypeScript-versioner före 5 bör de aktiveras med egenskapen `experimentalDecorators` i din `tsconfig.json` eller genom att använda `--experimentalDecorators` på kommandoraden (men följande exempel kommer inte att fungera). Några vanliga användningsfall för dekoratörer inkluderar: * Övervakning av egenskapsändringar. * Övervakning av metodanrop. * Tillägg av extra egenskaper eller metoder. * Validering vid körning. * Automatisk serialisering och deserialisering. * Loggning. * Auktorisering och autentisering. * Felhantering. Observera: Dekoratörer för version 5 tillåter inte dekorering av parametrar. Typer av dekoratörer: #### Klassdekoratörer Klassdekoratörer är användbara för att utöka en befintlig klass, till exempel genom att lägga till egenskaper eller metoder, eller samla instanser av en klass. I följande exempel lägger vi till en `toString`-metod som konverterar klassen till en strängrepresentation. ```typescript type Constructor = new (...args: any[]) => T; function toString( Value: Class, context: ClassDecoratorContext ) { return class extends Value { constructor(...args: any[]) { super(...args); console.log(JSON.stringify(this)); console.log(JSON.stringify(context)); } }; } @toString class Person { name: string; constructor(name: string) { this.name = name; } greet() { return 'Hello, ' + this.name; } } const person = new Person('Simon'); /* Logs: {"name":"Simon"} {"kind":"class","name":"Person"} */ ``` #### Egenskapsdekoratör Egenskapsdekoratörer är användbara för att ändra beteendet hos en egenskap, till exempel genom att ändra initieringsvärden. I följande kod har vi ett skript som ställer in en egenskap till att alltid vara i versaler: ```typescript function upperCase( target: undefined, context: ClassFieldDecoratorContext ) { return function (this: T, value: string) { return value.toUpperCase(); }; } class MyClass { @upperCase prop1 = 'hello!'; } console.log(new MyClass().prop1); // Logs: HELLO! ``` #### Metoddekoratör Metoddekoratörer låter dig ändra eller förbättra beteendet hos metoder. Nedan följer ett exempel på en enkel logger: ```typescript function log( target: (this: This, ...args: Args) => Return, context: ClassMethodDecoratorContext< This, (this: This, ...args: Args) => Return > ) { const methodName = String(context.name); function replacementMethod(this: This, ...args: Args): Return { console.log(`LOG: Entering method '${methodName}'.`); const result = target.call(this, ...args); console.log(`LOG: Exiting method '${methodName}'.`); return result; } return replacementMethod; } class MyClass { @log sayHello() { console.log('Hello!'); } } new MyClass().sayHello(); ``` Det loggar: ```shell LOG: Entering method 'sayHello'. Hello! LOG: Exiting method 'sayHello'. ``` #### Getter- och setter-dekoratörer Getter- och setter-dekoratörer låter dig ändra eller förbättra beteendet hos klass-accessorer. De är användbara, till exempel, för validering av egenskapstilldelningar. Här är ett enkelt exempel på en getter-dekoratör: ```typescript function range(min: number, max: number) { return function ( target: (this: This) => Return, context: ClassGetterDecoratorContext ) { return function (this: This): Return { const value = target.call(this); if (value < min || value > max) { throw 'Invalid'; } Object.defineProperty(this, context.name, { value, enumerable: true, }); return value; }; }; } class MyClass { private _value = 0; constructor(value: number) { this._value = value; } @range(1, 100) get getValue(): number { return this._value; } } const obj = new MyClass(10); console.log(obj.getValue); // Valid: 10 const obj2 = new MyClass(999); console.log(obj2.getValue); // Throw: Invalid! ``` #### Dekoratörmetadata Dekoratörmetadata förenklar processen för dekoratörer att tillämpa och använda metadata i valfri klass. De kan komma åt en ny metadataegenskap på kontextobjektet, som kan fungera som en nyckel för både primitiva värden och objekt. Metadatainformation kan nås på klassen via `Symbol.metadata`. Metadata kan användas för olika ändamål, till exempel felsökning, serialisering eller beroendeinjektion med dekoratörer. ```typescript //@ts-ignore Symbol.metadata ??= Symbol('Symbol.metadata'); // Simple polify type Context = | ClassFieldDecoratorContext | ClassAccessorDecoratorContext | ClassMethodDecoratorContext; // Context contains property metadata: DecoratorMetadata function setMetadata(_target: any, context: Context) { // Set the metadata object with a primitive value context.metadata[context.name] = true; } class MyClass { @setMetadata a = 123; @setMetadata accessor b = 'b'; @setMetadata fn() {} } const metadata = MyClass[Symbol.metadata]; // Get metadata information console.log(JSON.stringify(metadata)); // {"bar":true,"baz":true,"foo":true} ``` ### Arv Arv avser mekanismen genom vilken en klass kan ärva egenskaper och metoder från en annan klass, känd som basklassen eller superklassen. Den härledda klassen, även kallad barnklassen eller underklassen, kan utöka och specialisera basklassens funktionalitet genom att lägga till nya egenskaper och metoder eller åsidosätta befintliga. ```typescript class Animal { name: string; constructor(name: string) { this.name = name; } speak(): void { console.log('The animal makes a sound'); } } class Dog extends Animal { breed: string; constructor(name: string, breed: string) { super(name); this.breed = breed; } speak(): void { console.log('Woof! Woof!'); } } // Create an instance of the base class const animal = new Animal('Generic Animal'); animal.speak(); // The animal makes a sound // Create an instance of the derived class const dog = new Dog('Max', 'Labrador'); dog.speak(); // Woof! Woof!" ``` TypeScript stöder inte multipelt arv i traditionell bemärkelse utan tillåter istället arv från en enda basklass. TypeScript stöder flera gränssnitt. Ett gränssnitt kan definiera ett kontrakt för strukturen hos ett objekt, och en klass kan implementera flera gränssnitt. Detta gör det möjligt för en klass att ärva beteende och struktur från flera källor. ```typescript interface Flyable { fly(): void; } interface Swimmable { swim(): void; } class FlyingFish implements Flyable, Swimmable { fly() { console.log('Flying...'); } swim() { console.log('Swimming...'); } } const flyingFish = new FlyingFish(); flyingFish.fly(); flyingFish.swim(); ``` Nyckelordet `class` i TypeScript, liknande JavaScript, kallas ofta för syntaktiskt socker. Det introducerades i ECMAScript 2015 (ES6) för att erbjuda en mer välbekant syntax för att skapa och arbeta med objekt på ett klassbaserat sätt. Det är dock viktigt att notera att TypeScript, som en utökning av JavaScript, slutligen kompileras ner till JavaScript, som i grunden förblir prototypbaserat. ### Statiska medlemmar TypeScript har statiska medlemmar. För att komma åt de statiska medlemmarna i en klass kan du använda klassnamnet följt av en punkt, utan att behöva skapa ett objekt. ```typescript class OfficeWorker { static memberCount: number = 0; constructor(private name: string) { OfficeWorker.memberCount++; } } const w1 = new OfficeWorker('James'); const w2 = new OfficeWorker('Simon'); const total = OfficeWorker.memberCount; console.log(total); // 2 ``` ### Egenskapsinitiering Det finns flera sätt att initiera egenskaper för en klass i TypeScript: Inline: I följande exempel kommer dessa initiala värden att användas när en instans av klassen skapas. ```typescript class MyClass { property1: string = 'default value'; property2: number = 42; } ``` I konstruktorn: ```typescript class MyClass { property1: string; property2: number; constructor() { this.property1 = 'default value'; this.property2 = 42; } } ``` Använda konstruktorparametrar: ```typescript class MyClass { constructor( private property1: string = 'default value', public property2: number = 42 ) { // There is no need to assign the values to the properties explicitly. } log() { console.log(this.property2); } } const x = new MyClass(); x.log(); ``` ### Metodöverlagring Metodöverlagring gör det möjligt för en klass att ha flera metoder med samma namn men olika parametertyper eller ett annat antal parametrar. Detta gör att vi kan anropa en metod på olika sätt baserat på de argument som skickas. ```typescript class MyClass { add(a: number, b: number): number; // Overload signature 1 add(a: string, b: string): string; // Overload signature 2 add(a: number | string, b: number | string): number | string { if (typeof a === 'number' && typeof b === 'number') { return a + b; } if (typeof a === 'string' && typeof b === 'string') { return a.concat(b); } throw new Error('Invalid arguments'); } } const r = new MyClass(); console.log(r.add(10, 5)); // Logs 15 ``` ================================================ FILE: website/src/content/docs/sv-se/book/common-built-in-js-objects.md ================================================ --- title: Vanliga inbyggda JS-objekt sidebar: order: 50 label: 50. Vanliga inbyggda JS-objekt --- TypeScript är en utökning av JavaScript och inkluderar alla vanligt använda inbyggda JavaScript-objekt. Du kan hitta en utförlig lista över dessa objekt på Mozilla Developer Networks (MDN) dokumentationswebbplats: [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects) Här är en lista över några vanligt använda inbyggda JavaScript-objekt: * Function * Object * Boolean * Error * Number * BigInt * Math * Date * String * RegExp * Array * Map * Set * Promise * Intl ================================================ FILE: website/src/content/docs/sv-se/book/conditional-types.md ================================================ --- title: Villkorliga typer sidebar: order: 39 label: 39. Villkorliga typer --- Villkorliga typer är ett sätt att skapa en typ som beror på ett villkor, där typen som ska skapas bestäms baserat på resultatet av villkoret. De definieras med nyckelordet `extends` och en ternär operator för att villkorligt välja mellan två typer. ```typescript type IsArray = T extends any[] ? true : false; const myArray = [1, 2, 3]; const myNumber = 42; type IsMyArrayAnArray = IsArray; // Type true type IsMyNumberAnArray = IsArray; // Type false ``` ================================================ FILE: website/src/content/docs/sv-se/book/control-flow-analysis.md ================================================ --- title: Kontrollflödesanalys sidebar: order: 22 label: 22. Kontrollflödesanalys --- Kontrollflödesanalys i TypeScript är ett sätt att statiskt analysera kodflödet för att härleda typer av variabler, vilket gör det möjligt för kompilatorn att avsmalma typerna av dessa variabler efter behov, baserat på resultaten av analysen. Före TypeScript 4.4 tillämpades kodflödesanalys enbart på kod inom en if-sats, men från och med TypeScript 4.4 kan den även tillämpas på villkorliga uttryck och diskriminantegenskapsåtkomster som indirekt refereras genom const-variabler. Till exempel: ```typescript const f1 = (x: unknown) => { const isString = typeof x === 'string'; if (isString) { x.length; } }; const f2 = ( obj: { kind: 'foo'; foo: string } | { kind: 'bar'; bar: number } ) => { const isFoo = obj.kind === 'foo'; if (isFoo) { obj.foo; } else { obj.bar; } }; ``` Några exempel där avsmalning inte sker: ```typescript const f1 = (x: unknown) => { let isString = typeof x === 'string'; if (isString) { x.length; // Error, no narrowing because isString it is not const } }; const f6 = ( obj: { kind: 'foo'; foo: string } | { kind: 'bar'; bar: number } ) => { const isFoo = obj.kind === 'foo'; obj = obj; if (isFoo) { obj.foo; // Error, no narrowing because obj is assigned in function body } }; ``` Observera: Upp till fem nivåer av indirektion analyseras i villkorliga uttryck. ================================================ FILE: website/src/content/docs/sv-se/book/differences-between-type-and-interface.md ================================================ --- title: Skillnader mellan Type och Interface sidebar: order: 53 label: 53. Skillnader mellan Type och Interface --- Deklarationssammanslagning (augmentering): Interface stöder deklarationssammanslagning, vilket innebär att du kan definiera flera interface med samma namn, och TypeScript kommer att slå samman dem till ett enda interface med de kombinerade egenskaperna och metoderna. Å andra sidan stöder typer inte deklarationssammanslagning. Detta kan vara användbart när du vill lägga till extra funktionalitet eller anpassa befintliga typer utan att ändra de ursprungliga definitionerna eller korrigera saknade eller felaktiga typer. ```typescript interface A { x: string; } interface A { y: string; } const j: A = { x: 'xx', y: 'yy', }; ``` Utökning av andra typer/interface: Både typer och interface kan utöka andra typer/interface, men syntaxen är annorlunda. Med interface använder du nyckelordet `extends` för att ärva egenskaper och metoder från andra interface. Ett interface kan dock inte utöka en komplex typ som en union-typ. ```typescript interface A { x: string; y: number; } interface B extends A { z: string; } const car: B = { x: 'x', y: 123, z: 'z', }; ``` För typer använder du operatorn & för att kombinera flera typer till en enda typ (intersection). ```typescript interface A { x: string; y: number; } type B = A & { j: string; }; const c: B = { x: 'x', y: 123, j: 'j', }; ``` Union- och Intersection-typer: Typer är mer flexibla när det gäller att definiera union- och intersection-typer. Med nyckelordet `type` kan du enkelt skapa union-typer med operatorn `|` och intersection-typer med operatorn `&`. Även om interface också kan representera union-typer indirekt, har de inget inbyggt stöd för intersection-typer. ```typescript type Department = 'dep-x' | 'dep-y'; // Union type Person = { name: string; age: number; }; type Employee = { id: number; department: Department; }; type EmployeeInfo = Person & Employee; // Intersection ``` Exempel med interface: ```typescript interface A { x: 'x'; } interface B { y: 'y'; } type C = A | B; // Union of interfaces ``` ================================================ FILE: website/src/content/docs/sv-se/book/discriminated-unions.md ================================================ --- title: Diskriminerade unioner sidebar: order: 24 label: 24. Diskriminerade unioner --- Diskriminerade unioner i TypeScript är en typ av unionstyp som använder en gemensam egenskap, känd som diskriminanten, för att avsmalma uppsättningen av möjliga typer för unionen. ```typescript type Square = { kind: 'square'; // Discriminant size: number; }; type Circle = { kind: 'circle'; // Discriminant radius: number; }; type Shape = Square | Circle; const area = (shape: Shape) => { switch (shape.kind) { case 'square': return Math.pow(shape.size, 2); case 'circle': return Math.PI * Math.pow(shape.radius, 2); } }; const square: Square = { kind: 'square', size: 5 }; const circle: Circle = { kind: 'circle', radius: 2 }; console.log(area(square)); // 25 console.log(area(circle)); // 12.566370614359172 ``` ================================================ FILE: website/src/content/docs/sv-se/book/distributive-conditional-types.md ================================================ --- title: Distributiva villkorliga typer sidebar: order: 40 label: 40. Distributiva villkorliga typer --- Distributiva villkorliga typer är en funktion som gör det möjligt att distribuera en typ över en union av typer, genom att tillämpa en transformation på varje medlem av unionen individuellt. Detta kan vara särskilt användbart vid arbete med mappade typer eller typer av högre ordning. ```typescript type Nullable = T extends any ? T | null : never; type NumberOrBool = number | boolean; type NullableNumberOrBool = Nullable; // number | boolean | null ``` ================================================ FILE: website/src/content/docs/sv-se/book/downloads-and-website.md ================================================ --- title: Nedladdningar och webbplats sidebar: order: 3 label: 3. Nedladdningar och webbplats --- Du kan också ladda ner Epub-versionen: [https://github.com/gibbok/typescript-book/tree/main/downloads](https://github.com/gibbok/typescript-book/tree/main/downloads) En onlineversion finns tillgänglig på: [https://gibbok.github.io/typescript-book](https://gibbok.github.io/typescript-book) ================================================ FILE: website/src/content/docs/sv-se/book/enums.md ================================================ --- title: Enums sidebar: order: 19 label: 19. Enums --- I TypeScript är en `enum` en uppsättning namngivna konstantvärden. ```typescript enum Color { Red = '#ff0000', Green = '#00ff00', Blue = '#0000ff', } ``` Enums kan definieras på olika sätt: ### Numeriska enums I TypeScript är en numerisk Enum en Enum där varje konstant tilldelas ett numeriskt värde, med start från 0 som standard. ```typescript enum Size { Small, // value starts from 0 Medium, Large, } ``` Det är möjligt att ange anpassade värden genom att explicit tilldela dem: ```typescript enum Size { Small = 10, Medium, Large, } console.log(Size.Medium); // 11 ``` ### Sträng-enums I TypeScript är en sträng-Enum en Enum där varje konstant tilldelas ett strängvärde. ```typescript enum Language { English = 'EN', Spanish = 'ES', } ``` Notera: TypeScript tillåter användning av heterogena Enums där sträng- och numeriska medlemmar kan samexistera. ### Konstanta enums En konstant enum i TypeScript är en speciell typ av Enum där alla värden är kända vid kompileringstid och infogas överallt där enum:en används, vilket resulterar i mer effektiv kod. ```typescript const enum Language { English = 'EN', Spanish = 'ES', } console.log(Language.English); ``` Kommer att kompileras till: ```typescript console.log('EN' /* Language.English */); ``` Noteringar: Konstanta Enums har hårdkodade värden som raderar Enum:en, vilket kan vara mer effektivt i fristående bibliotek men är i allmänhet inte önskvärt. Dessutom kan konstanta enums inte ha beräknade medlemmar. ### Omvänd mappning I TypeScript avser omvänd mappning i Enums möjligheten att hämta Enum-medlemmens namn från dess värde. Som standard har Enum-medlemmar framåtmappningar från namn till värde, men omvända mappningar kan skapas genom att explicit ange värden för varje medlem. Omvända mappningar är användbara när du behöver slå upp en Enum-medlem efter dess värde, eller när du behöver iterera över alla Enum-medlemmar. Observera att bara numeriska Enum-medlemmar genererar omvända mappningar, medan sträng-Enum-medlemmar inte genererar någon omvänd mappning alls. Följande enum: ```typescript enum Grade { A = 90, B = 80, C = 70, F = 'fail', } ``` Kompileras till: ```javascript 'use strict'; var Grade; (function (Grade) { Grade[(Grade['A'] = 90)] = 'A'; Grade[(Grade['B'] = 80)] = 'B'; Grade[(Grade['C'] = 70)] = 'C'; Grade['F'] = 'fail'; })(Grade || (Grade = {})); ``` Därför fungerar mappning av värden till nycklar för numeriska enum-medlemmar, men inte för sträng-enum-medlemmar: ```typescript enum Grade { A = 90, B = 80, C = 70, F = 'fail', } const myGrade = Grade.A; console.log(Grade[myGrade]); // A console.log(Grade[90]); // A const failGrade = Grade.F; console.log(failGrade); // fail console.log(Grade[failGrade]); // Element implicitly has an 'any' type because index expression is not of type 'number'. ``` ### Omgivande enums En omgivande enum i TypeScript är en typ av Enum som definieras i en deklarationsfil (*.d.ts) utan en associerad implementation. Den låter dig definiera en uppsättning namngivna konstanter som kan användas på ett typsäkert sätt över olika filer utan att behöva importera implementationsdetaljerna i varje fil. ### Beräknade och konstanta medlemmar I TypeScript är en beräknad medlem en medlem av en Enum som har ett värde som beräknas vid körning, medan en konstant medlem är en medlem vars värde sätts vid kompileringstid och inte kan ändras under körning. Beräknade medlemmar är tillåtna i vanliga Enums, medan konstanta medlemmar är tillåtna i både vanliga och const enums. ```typescript // Constant members enum Color { Red = 1, Green = 5, Blue = Red + Green, } console.log(Color.Blue); // 6 generation at compilation time ``` ```typescript // Computed members enum Color { Red = 1, Green = Math.pow(2, 2), Blue = Math.floor(Math.random() * 3) + 1, } console.log(Color.Blue); // random number generated at run time ``` Enums betecknas av unioner som består av deras medlemstyper. Värdena för varje medlem kan bestämmas genom konstanta eller icke-konstanta uttryck, där medlemmar med konstanta värden tilldelas literaltyper. För att illustrera, betrakta deklarationen av typ E och dess undertyper E.A, E.B och E.C. I detta fall representerar E unionen E.A | E.B | E.C. ```typescript const identity = (value: number) => value; enum E { A = 2 * 5, // Numeric literal B = 'bar', // String literal C = identity(42), // Opaque computed } console.log(E.C); //42 ``` ================================================ FILE: website/src/content/docs/sv-se/book/erased-structural-types.md ================================================ --- title: Raderade strukturella typer sidebar: order: 56 label: 56. Raderade strukturella typer --- I TypeScript behöver objekt inte matcha en specifik, exakt typ. Till exempel, om vi skapar ett objekt som uppfyller ett gränssnitts krav, kan vi använda det objektet på platser där det gränssnittet krävs, även om det inte finns någon explicit koppling mellan dem. Exempel: ```typescript type NameProp1 = { prop1: string; }; function log(x: NameProp1) { console.log(x.prop1); } const obj = { prop2: 123, prop1: 'Origin', }; log(obj); // Valid ``` ================================================ FILE: website/src/content/docs/sv-se/book/exhaustiveness-checking.md ================================================ --- title: Uttömmande kontroll sidebar: order: 26 label: 26. Uttömmande kontroll --- Uttömmande kontroll är en funktion i TypeScript som säkerställer att alla möjliga fall av en diskriminerad union hanteras i en `switch`-sats eller en `if`-sats. ```typescript type Direction = 'up' | 'down'; const move = (direction: Direction) => { switch (direction) { case 'up': console.log('Moving up'); break; case 'down': console.log('Moving down'); break; default: const exhaustiveCheck: never = direction; console.log(exhaustiveCheck); // This line will never be executed } }; ``` `never`-typen används för att säkerställa att default-fallet är uttömmande och att TypeScript kommer att ge ett fel om ett nytt värde läggs till i Direction-typen utan att det hanteras i switch-satsen. ================================================ FILE: website/src/content/docs/sv-se/book/exploring-the-type-system.md ================================================ --- title: Utforska typsystemet sidebar: order: 9 label: 9. Utforska typsystemet --- ### TypeScript-språktjänsten TypeScript-språktjänsten, även känd som tsserver, erbjuder olika funktioner såsom felrapportering, diagnostik, kompilera-vid-sparning, namnbyte, gå till definition, kompletteringslistor, signaturhjälp och mer. Den används främst av integrerade utvecklingsmiljöer (IDE:er) för att ge IntelliSense-stöd. Den integreras sömlöst med Visual Studio Code och används av verktyg som Conquer of Completion (Coc). Utvecklare kan utnyttja ett dedikerat API och skapa sina egna anpassade språktjänstplugin för att förbättra TypeScript-redigeringsupplevelsen. Detta kan vara särskilt användbart för att implementera speciella linting-funktioner eller möjliggöra automatisk komplettering för ett anpassat mallspråk. Ett exempel på ett verkligt anpassat plugin är "typescript-styled-plugin", som tillhandahåller syntaxfelrapportering och IntelliSense-stöd för CSS-egenskaper i styled components. För mer information och snabbstartsguider kan du hänvisa till den officiella TypeScript-wikin på GitHub: [https://github.com/microsoft/TypeScript/wiki/](https://github.com/microsoft/TypeScript/wiki/) ### Strukturell typning TypeScript är baserat på ett strukturellt typsystem. Detta innebär att kompatibiliteten och ekvivalensen hos typer bestäms av typens faktiska struktur eller definition, snarare än dess namn eller plats för deklaration, som i nominativa typsystem som C# eller C. TypeScripts strukturella typsystem designades baserat på hur JavaScripts dynamiska duck typing-system fungerar vid körning. Följande exempel är giltig TypeScript-kod. Som du kan observera har "X" och "Y" samma medlem "a", även om de har olika deklarationsnamn. Typerna bestäms av deras strukturer, och i detta fall, eftersom strukturerna är desamma, är de kompatibla och giltiga. ```typescript type X = { a: string; }; type Y = { a: string; }; const x: X = { a: 'a' }; const y: Y = x; // Valid ``` ### Grundläggande jämförelseregler i TypeScript TypeScripts jämförelseprocess är rekursiv och utförs på typer som är nästlade på valfri nivå. En typ "X" är kompatibel med "Y" om "Y" har åtminstone samma medlemmar som "X". ```typescript type X = { a: string; }; const y = { a: 'A', b: 'B' }; // Valid, as it has at least the same members as X const r: X = y; ``` Funktionsparametrar jämförs efter typer, inte efter deras namn: ```typescript type X = (a: number) => void; type Y = (a: number) => void; let x: X = (j: number) => undefined; let y: Y = (k: number) => undefined; y = x; // Valid x = y; // Valid ``` Funktionens returtyper måste vara desamma: ```typescript type X = (a: number) => undefined; type Y = (a: number) => number; let x: X = (a: number) => undefined; let y: Y = (a: number) => 1; y = x; // Invalid x = y; // Invalid ``` Returtypen för en källfunktion måste vara en undertyp av returtypen för en målfunktion: ```typescript let x = () => ({ a: 'A' }); let y = () => ({ a: 'A', b: 'B' }); x = y; // Valid y = x; // Invalid member b is missing ``` Att utelämna funktionsparametrar är tillåtet, eftersom det är vanlig praxis i JavaScript, till exempel vid användning av "Array.prototype.map()": ```typescript [1, 2, 3].map((element, _index, _array) => element + 'x'); ``` Därför är följande typdeklarationer helt giltiga: ```typescript type X = (a: number) => undefined; type Y = (a: number, b: number) => undefined; let x: X = (a: number) => undefined; let y: Y = (a: number) => undefined; // Missing b parameter y = x; // Valid ``` Eventuella ytterligare valfria parametrar i källtypen är giltiga: ```typescript type X = (a: number, b?: number, c?: number) => undefined; type Y = (a: number) => undefined; let x: X = a => undefined; let y: Y = a => undefined; y = x; // Valid x = y; //Valid ``` Eventuella valfria parametrar i måltypen utan motsvarande parametrar i källtypen är giltiga och utgör inte ett fel: ```typescript type X = (a: number) => undefined; type Y = (a: number, b?: number) => undefined; let x: X = a => undefined; let y: Y = a => undefined; y = x; // Valid x = y; // Valid ``` Rest-parametern behandlas som en oändlig serie av valfria parametrar: ```typescript type X = (a: number, ...rest: number[]) => undefined; let x: X = a => undefined; //valid ``` Funktioner med överlagringar är giltiga om överlagringssignaturen är kompatibel med dess implementeringssignatur: ```typescript function x(a: string): void; function x(a: string, b: number): void; function x(a: string, b?: number): void { console.log(a, b); } x('a'); // Valid x('a', 1); // Valid function y(a: string): void; // Invalid, not compatible with implementation signature function y(a: string, b: number): void; function y(a: string, b: number): void { console.log(a, b); } y('a'); y('a', 1); ``` Jämförelse av funktionsparametrar lyckas om käll- och målparametrarna kan tilldelas supertyper eller undertyper (bivarians). ```typescript // Supertype class X { a: string; constructor(value: string) { this.a = value; } } // Subtype class Y extends X {} // Subtype class Z extends X {} type GetA = (x: X) => string; const getA: GetA = x => x.a; // Bivariance does accept supertypes console.log(getA(new X('x'))); // Valid console.log(getA(new Y('Y'))); // Valid console.log(getA(new Z('z'))); // Valid ``` Enums är jämförbara och giltiga med tal och vice versa, men att jämföra Enum-värden från olika Enum-typer är ogiltigt. ```typescript enum X { A, B, } enum Y { A, B, C, } const xa: number = X.A; // Valid const ya: Y = 0; // Valid X.A === Y.A; // Invalid ``` Instanser av en klass genomgår en kompatibilitetskontroll för sina privata och skyddade medlemmar: ```typescript class X { public a: string; constructor(value: string) { this.a = value; } } class Y { private a: string; constructor(value: string) { this.a = value; } } let x: X = new Y('y'); // Invalid ``` Jämförelsekontrollen tar inte hänsyn till den olika arvshierarkin, till exempel: ```typescript class X { public a: string; constructor(value: string) { this.a = value; } } class Y extends X { public a: string; constructor(value: string) { super(value); this.a = value; } } class Z { public a: string; constructor(value: string) { this.a = value; } } let x: X = new X('x'); let y: Y = new Y('y'); let z: Z = new Z('z'); x === y; // Valid x === z; // Valid even if z is from a different inheritance hierarchy ``` Generics jämförs med hjälp av deras strukturer baserat på den resulterande typen efter tillämpning av den generiska parametern. Bara slutresultatet jämförs som en icke-generisk typ. ```typescript interface X { a: T; } let x: X = { a: 1 }; let y: X = { a: 'a' }; x === y; // Invalid as the type argument is used in the final structure ``` ```typescript interface X {} const x: X = 1; const y: X = 'a'; x === y; // Valid as the type argument is not used in the final structure ``` När generics inte har sitt typargument specificerat behandlas alla ospecificerade argument som typer med "any": ```typescript type X = (x: T) => T; type Y = (y: K) => K; let x: X = x => x; let y: Y = y => y; x = y; // Valid ``` Kom ihåg: ```typescript let a: number = 1; let b: number = 2; a = b; // Valid, everything is assignable to itself let c: any; c = 1; // Valid, all types are assignable to any let d: unknown; d = 1; // Valid, all types are assignable to unknown let e: unknown; let e1: unknown = e; // Valid, unknown is only assignable to itself and any let e2: any = e; // Valid let e3: number = e; // Invalid let f: never; f = 1; // Invalid, nothing is assignable to never let g: void; let g1: any; g = 1; // Invalid, void is not assignable to or from anything expect any g = g1; // Valid ``` Observera att när "strictNullChecks" är aktiverat behandlas "null" och "undefined" på liknande sätt som "void"; annars liknar de "never". ### Typer som mängder I TypeScript är en typ en mängd av möjliga värden. Denna mängd kallas även typens domän. Varje värde av en typ kan ses som ett element i en mängd. En typ fastställer de begränsningar som varje element i mängden måste uppfylla för att betraktas som en medlem av den mängden. TypeScripts primära uppgift är att kontrollera och verifiera om en mängd är en delmängd av en annan. TypeScript stöder olika typer av mängder: | Mängdterm | TypeScript | Anteckningar | | ---------------------- | ------------------------------- | ------------------------------------------------------------------------------------------------------------------ | | Tom mängd | never | "never" innehåller ingenting förutom sig själv | | Enelement-mängd | undefined / null / literal type | | | Ändlig mängd | boolean / union | | | Oändlig mängd | string / number / object | | | Universell mängd | any / unknown | Varje element är medlem i "any" och varje mängd är en delmängd av den / "unknown" är en typsäker motsvarighet till "any" | Här är några exempel: | TypeScript | Mängdterm | Exempel | | --------------------- | ---------------------- | ------------------------------------------------------------------------------- | | never | ∅ (tom mängd) | const x: never = 'x'; // Error: Type 'string' is not assignable to type 'never' | | | | | Literal type | Enelement-mängd | type X = 'X'; | | | | type Y = 7; | | | | | Värde tilldelbart till T | Värde ∈ T (medlem av) | type XY = 'X' \| 'Y'; | | | | const x: XY = 'X'; | | | | | T1 tilldelbart till T2 | T1 ⊆ T2 (delmängd av) | type XY = 'X' \| 'Y'; | | | | const x: XY = 'X'; | | | | const j: XY = 'J'; // Type '"J"' is not assignable to type 'XY'. | | | | | | T1 extends T2 | T1 ⊆ T2 (delmängd av) | type X = 'X' extends string ? true : false; | | | | | T1 \| T2 | T1 ∪ T2 (union) | type XY = 'X' \| 'Y'; | | | | type JK = 1 \| 2; | | | | | T1 & T2 | T1 ∩ T2 (snitt) | type X = \{ a: string \} | | | | type Y = \{ b: string \} | | | | type XY = X & Y | | | | const x: XY = \{ a: 'a', b: 'b' \} | | | | | unknown | Universell mängd | const x: unknown = 1 | En union, (T1 | T2) skapar en bredare mängd (båda): ```typescript type X = { a: string; }; type Y = { b: string; }; type XY = X | Y; const r: XY = { a: 'a', b: 'x' }; // Valid ``` En intersektion, (T1 & T2) skapar en smalare mängd (endast delade): ```typescript type X = { a: string; }; type Y = { a: string; b: string; }; type XY = X & Y; const r: XY = { a: 'a' }; // Invalid const j: XY = { a: 'a', b: 'b' }; // Valid ``` Nyckelordet `extends` kan betraktas som "delmängd av" i detta sammanhang. Det sätter en begränsning för en typ. När extends används med en generisk typ, behandlas den generiska typen som en oändlig mängd och begränsas till en mer specifik typ. Observera att `extends` inte har något att göra med hierarki i OOP-bemärkelse (det finns inget sådant koncept i TypeScript). TypeScript arbetar med mängder och har ingen strikt hierarki. Faktum är att, som i exemplet nedan, två typer kan överlappa utan att någon av dem är en undertyp av den andra (TypeScript betraktar strukturen, formen på objekten). ```typescript interface X { a: string; } interface Y extends X { b: string; } interface Z extends Y { c: string; } const z: Z = { a: 'a', b: 'b', c: 'c' }; interface X1 { a: string; } interface Y1 { a: string; b: string; } interface Z1 { a: string; b: string; c: string; } const z1: Z1 = { a: 'a', b: 'b', c: 'c' }; const r: Z1 = z; // Valid ``` ### Tilldela en typ: Typdeklarationer och Typpåståenden En typ kan tilldelas på olika sätt i TypeScript: #### Typdeklaration I följande exempel använder vi x: X (": Type") för att deklarera en typ för variabeln x. ```typescript type X = { a: string; }; // Type declaration const x: X = { a: 'a', }; ``` Om variabeln inte har det angivna formatet kommer TypeScript att rapportera ett fel. Till exempel: ```typescript type X = { a: string; }; const x: X = { a: 'a', b: 'b', // Error: Object literal may only specify known properties }; ``` #### Typpåstående Det är möjligt att lägga till ett påstående genom att använda nyckelordet `as`. Detta talar om för kompilatorn att utvecklaren har mer information om en typ och tystar eventuella fel som kan uppstå. Till exempel: ```typescript type X = { a: string; }; const x = { a: 'a', b: 'b', } as X; ``` I exemplet ovan påstås objektet x ha typen X med hjälp av nyckelordet as. Detta informerar TypeScript-kompilatorn om att objektet överensstämmer med den angivna typen, även om det har en extra egenskap b som inte finns i typdefinitionen. Typpåståenden är användbara i situationer där en mer specifik typ behöver anges, särskilt vid arbete med DOM:en. Till exempel: ```typescript const myInput = document.getElementById('my_input') as HTMLInputElement; ``` Här används typpåståendet as HTMLInputElement för att tala om för TypeScript att resultatet av getElementById ska behandlas som ett HTMLInputElement. Typpåståenden kan också användas för att mappa om nycklar, som visas i exemplet nedan med malliteraler: ```typescript type J = { [Property in keyof Type as `prefix_${string & Property}`]: () => Type[Property]; }; type X = { a: string; b: number; }; type Y = J; ``` I detta exempel använder typen `J` en mappad typ med en malliteral för att mappa om nycklarna i Type. Den skapar nya egenskaper med ett "prefix_" tillagt till varje nyckel, och deras motsvarande värden är funktioner som returnerar de ursprungliga egenskapsvärdena. Det är värt att notera att när man använder ett typpåstående kommer TypeScript inte att utföra kontroll av överskottsegenskaper. Därför är det generellt att föredra att använda en typdeklaration när objektets struktur är känd i förväg. #### Omgivande deklarationer Omgivande deklarationer är filer som beskriver typer för JavaScript-kod. De har filnamnsformatet `.d.ts`. De importeras vanligtvis och används för att annotera befintliga JavaScript-bibliotek eller för att lägga till typer till befintliga JS-filer i ditt projekt. Många vanliga bibliotekstyper finns på: [https://github.com/DefinitelyTyped/DefinitelyTyped/](https://github.com/DefinitelyTyped/DefinitelyTyped/) och kan installeras med: ```shell npm install --save-dev @types/library-name ``` För dina egna omgivande deklarationer kan du importera dem med "triple-slash"-referensen: ```typescript /// ``` Du kan använda omgivande deklarationer även i JavaScript-filer med `// @ts-check`. Nyckelordet `declare` möjliggör typdefinitioner för befintlig JavaScript-kod utan att importera den, och fungerar som en platshållare för typer från en annan fil eller globalt. ### Egenskapskontroll och kontroll av överskottsegenskaper TypeScript bygger på ett strukturellt typsystem, men kontroll av överskottsegenskaper är en egenskap hos TypeScript som gör det möjligt att kontrollera om ett objekt har exakt de egenskaper som anges i typen. Kontroll av överskottsegenskaper utförs vid tilldelning av objektliteraler till variabler eller när de skickas som argument till funktionens överskottsegenskap, till exempel. ```typescript type X = { a: string; }; const y = { a: 'a', b: 'b' }; const x: X = y; // Valid because structural typing const w: X = { a: 'a', b: 'b' }; // Invalid because excess property checking ``` ### Svaga typer En typ anses vara svag när den inte innehåller annat än en uppsättning helt valfria egenskaper: ```typescript type X = { a?: string; b?: string; }; ``` TypeScript betraktar det som ett fel att tilldela något till en svag typ när det inte finns någon överlappning. Till exempel ger följande ett fel: ```typescript type Options = { a?: string; b?: string; }; const fn = (options: Options) => undefined; fn({ c: 'c' }); // Invalid ``` Även om det inte rekommenderas, är det möjligt att kringgå denna kontroll genom att använda typpåstående om det behövs: ```typescript type Options = { a?: string; b?: string; }; const fn = (options: Options) => undefined; fn({ c: 'c' } as Options); // Valid ``` Eller genom att lägga till `unknown` i indexsignaturen till den svaga typen: ```typescript type Options = { [prop: string]: unknown; a?: string; b?: string; }; const fn = (options: Options) => undefined; fn({ c: 'c' }); // Valid ``` ### Strikt kontroll av objektliteraler (Freshness) Strikt kontroll av objektliteraler, ibland kallad "freshness", är en funktion i TypeScript som hjälper till att fånga överskotts- eller felstavade egenskaper som annars skulle gå obemärkta vid normala strukturella typkontroller. När man skapar en objektliteral betraktar TypeScript-kompilatorn den som "fresh". Om objektliteralen tilldelas till en variabel eller skickas som parameter kommer TypeScript att ge ett fel om objektliteralen anger egenskaper som inte finns i måltypen. Dock försvinner "freshness" när en objektliteral breddas eller ett typpåstående används. Här är några exempel för att illustrera: ```typescript type X = { a: string }; type Y = { a: string; b: string }; let x: X; x = { a: 'a', b: 'b' }; // Freshness check: Invalid assignment var y: Y; y = { a: 'a', bx: 'bx' }; // Freshness check: Invalid assignment const fn = (x: X) => console.log(x.a); fn(x); fn(y); // Widening: No errors, structurally type compatible fn({ a: 'a', bx: 'b' }); // Freshness check: Invalid argument let c: X = { a: 'a' }; let d: Y = { a: 'a', b: '' }; c = d; // Widening: No Freshness check ``` ### Typinferens TypeScript kan härleda typer när ingen annotering tillhandahålls vid: * Variabelinitiering. * Medlemsinitiering. * Inställning av standardvärden för parametrar. * Funktionens returtyp. Till exempel: ```typescript let x = 'x'; // The type inferred is string ``` TypeScript-kompilatorn analyserar värdet eller uttrycket och bestämmer dess typ baserat på tillgänglig information. ### Mer avancerade inferenser När flera uttryck används vid typinferens letar TypeScript efter de "bästa gemensamma typerna". Till exempel: ```typescript let x = [1, 'x', 1, null]; // The type inferred is: (string | number | null)[] ``` Om kompilatorn inte kan hitta de bästa gemensamma typerna returnerar den en unionstyp. Till exempel: ```typescript let x = [new RegExp('x'), new Date()]; // Type inferred is: (RegExp | Date)[] ``` TypeScript använder "kontextuell typning" baserat på variabelns placering för att härleda typer. I följande exempel vet kompilatorn att `e` är av typen `MouseEvent` på grund av händelsetypen `click` som definieras i filen lib.d.ts, vilken innehåller omgivande deklarationer för olika vanliga JavaScript-konstruktioner och DOM:en: ```typescript window.addEventListener('click', function (e) {}); // The inferred type of e is MouseEvent ``` ### Typbreddning Typbreddning är den process där TypeScript tilldelar en typ till en variabel som initierats utan att en typannotering angavs. Den tillåter övergång från smal till bredare typ men inte tvärtom. I följande exempel: ```typescript let x = 'x'; // TypeScript infers as string, a wide type let y: 'y' | 'x' = 'y'; // y types is a union of literal types y = x; // Invalid Type 'string' is not assignable to type '"x" | "y"'. ``` TypeScript tilldelar `string` till `x` baserat på det enda värde som angavs vid initieringen (`x`), detta är ett exempel på breddning. TypeScript tillhandahåller sätt att kontrollera breddningsprocessen, till exempel genom att använda "const". ### Const Att använda nyckelordet `const` vid deklaration av en variabel resulterar i en smalare typinferens i TypeScript. Till exempel: ```typescript const x = 'x'; // TypeScript infers the type of x as 'x', a narrower type let y: 'y' | 'x' = 'y'; y = x; // Valid: The type of x is inferred as 'x' ``` Genom att använda `const` för att deklarera variabeln x, smalnas dess typ av till det specifika literalvärdet 'x'. Eftersom typen av x är avsmalnad kan den tilldelas till variabeln y utan något fel. Anledningen till att typen kan härledas är att `const`-variabler inte kan omtilldelas, så deras typ kan smalnas av till en specifik literaltyp, i detta fall literaltypen 'x'. #### Const-modifierare på typparametrar Från version 5.0 av TypeScript är det möjligt att ange attributet `const` på en generisk typparameter. Detta möjliggör härledning av den mest precisa typen möjligt. Låt oss se ett exempel utan att använda `const`: ```typescript function identity(value: T) { // No const here return value; } const values = identity({ a: 'a', b: 'b' }); // Type infered is: { a: string; b: string; } ``` Som du kan se härleddes egenskaperna `a` och `b` med typen `string`. Låt oss nu se skillnaden med `const`-versionen: ```typescript function identity(value: T) { // Using const modifier on type parameters return value; } const values = identity({ a: 'a', b: 'b' }); // Type infered is: { a: "a"; b: "b"; } ``` Nu kan vi se att egenskaperna `a` och `b` härleddes som `const`, så `a` och `b` behandlas som strängliteraler snarare än bara `string`-typer. #### Const-påstående Denna funktion låter dig deklarera en variabel med en mer precis literaltyp baserat på dess initieringsvärde, och signalerar till kompilatorn att värdet ska behandlas som en oföränderlig literal. Här är några exempel: På en enskild egenskap: ```typescript const v = { x: 3 as const, }; v.x = 3; ``` På ett helt objekt: ```typescript const v = { x: 1, y: 2, } as const; ``` Detta kan vara särskilt användbart vid definition av typen för en tupel: ```typescript const x = [1, 2, 3]; // number[] const y = [1, 2, 3] as const; // Tuple of readonly [1, 2, 3] ``` ### Explicit typannotering Vi kan vara specifika och ange en typ. I följande exempel är egenskapen `x` av typen `number`: ```typescript const v = { x: 1, // Inferred type: number (widening) }; v.x = 3; // Valid ``` Vi kan göra typannoteringen mer specifik genom att använda en union av literaltyper: ```typescript const v: { x: 1 | 2 | 3 } = { x: 1, // x is now a union of literal types: 1 | 2 | 3 }; v.x = 3; // Valid v.x = 100; // Invalid ``` ### Typavsmalnande Typavsmalnande är den process i TypeScript där en generell typ smalnas av till en mer specifik typ. Detta sker när TypeScript analyserar koden och avgör att vissa villkor eller operationer kan förfina typinformationen. Avsmalnande av typer kan ske på olika sätt, bland annat: #### Villkor Genom att använda villkorssatser, som `if` eller `switch`, kan TypeScript smalna av typen baserat på utfallet av villkoret. Till exempel: ```typescript let x: number | undefined = 10; if (x !== undefined) { x += 100; // The type is number, which had been narrowed by the condition } ``` #### Kasta eller returnera Att kasta ett fel eller returnera tidigt från en gren kan användas för att hjälpa TypeScript smalna av en typ. Till exempel: ```typescript let x: number | undefined = 10; if (x === undefined) { throw 'error'; } x += 100; ``` Andra sätt att smalna av typer i TypeScript inkluderar: * `instanceof`-operatorn: Används för att kontrollera om ett objekt är en instans av en specifik klass. * `in`-operatorn: Används för att kontrollera om en egenskap finns i ett objekt. * `typeof`-operatorn: Används för att kontrollera typen av ett värde vid körning. * Inbyggda funktioner som `Array.isArray()`: Används för att kontrollera om ett värde är en array. #### Diskriminerad union Att använda en "diskriminerad union" är ett mönster i TypeScript där en explicit "tagg" läggs till objekt för att skilja mellan olika typer inom en union. Detta mönster kallas också en "taggad union". I följande exempel representeras "taggen" av egenskapen "type": ```typescript type A = { type: 'type_a'; value: number }; type B = { type: 'type_b'; value: string }; const x = (input: A | B): string | number => { switch (input.type) { case 'type_a': return input.value + 100; // type is A case 'type_b': return input.value + 'extra'; // type is B } }; ``` #### Användardefinierade typvakter I fall där TypeScript inte kan avgöra en typ är det möjligt att skriva en hjälpfunktion känd som en "användardefinierad typvakt". I följande exempel kommer vi att använda ett typpredikat för att smalna av typen efter att viss filtrering har tillämpats: ```typescript const data = ['a', null, 'c', 'd', null, 'f']; const r1 = data.filter(x => x != null); // The type is (string | null)[], TypeScript was not able to infer the type properly const isValid = (item: string | null): item is string => item !== null; // Custom type guard const r2 = data.filter(isValid); // The type is fine now string[], by using the predicate type guard we were able to narrow the type ``` ================================================ FILE: website/src/content/docs/sv-se/book/extending-types.md ================================================ --- title: Utöka typer sidebar: order: 15 label: 15. Utöka typer --- Det är möjligt att utöka ett `interface` (kopiera medlemmar från en annan typ): ```typescript interface X { a: string; } interface Y extends X { b: string; } ``` Det är också möjligt att utöka från flera typer: ```typescript interface A { a: string; } interface B { b: string; } interface Y extends A, B { y: string; } ``` Nyckelordet `extends` fungerar bara på interface och klasser, för typer använd en intersektion: ```typescript type A = { a: number; }; type B = { b: number; }; type C = A & B; ``` Det är möjligt att utöka en typ med hjälp av en härledning men inte tvärtom: ```typescript type A = { a: string; }; interface B extends A { b: string; } ``` ================================================ FILE: website/src/content/docs/sv-se/book/fixed-length-tuple.md ================================================ --- title: Tuppel med fast längd sidebar: order: 30 label: 30. Tuppel med fast längd --- En tuppel med fast längd är en specifik typ av tuppel som tvingar fram ett fast antal element av specifika typer, och tillåter inte några ändringar av tuppelns längd efter att den har definierats. Tupplar med fast längd är användbara när du behöver representera en samling värden med ett specifikt antal element och specifika typer, och du vill säkerställa att längden och typerna av tuppeln inte kan ändras oavsiktligt. ```typescript const x = [10, 'hello'] as const; x.push(2); // Error ``` ================================================ FILE: website/src/content/docs/sv-se/book/generics.md ================================================ --- title: Generics sidebar: order: 55 label: 55. Generics --- Generics gör det möjligt att skapa återanvändbara komponenter och funktioner som kan arbeta med flera typer. Med generics kan du parameterisera typer, funktioner och gränssnitt, vilket gör att de kan arbeta med olika typer utan att explicit specificera dem i förväg. Generics gör det möjligt att göra koden mer flexibel och återanvändbar. ### Generisk typ För att definiera en generisk typ använder du vinkelparenteser (`<>`) för att specificera typparametrarna, till exempel: ```typescript function identity(arg: T): T { return arg; } const a = identity('x'); const b = identity(123); const getLen = (data: ReadonlyArray) => data.length; const len = getLen([1, 2, 3]); ``` ### Generiska klasser Generics kan även tillämpas på klasser, på så sätt kan de arbeta med flera typer genom att använda typparametrar. Detta är användbart för att skapa återanvändbara klassdefinitioner som kan arbeta med olika datatyper samtidigt som typsäkerheten bibehålls. ```typescript class Container { private item: T; constructor(item: T) { this.item = item; } getItem(): T { return this.item; } } const numberContainer = new Container(123); console.log(numberContainer.getItem()); // 123 const stringContainer = new Container('hello'); console.log(stringContainer.getItem()); // hello ``` ### Generiska begränsningar Generiska parametrar kan begränsas med nyckelordet `extends` följt av en typ eller ett gränssnitt som typparametern måste uppfylla. I följande exempel måste T innehålla en egenskap `length` för att vara giltig: ```typescript const printLen = (value: T): void => { console.log(value.length); }; printLen('Hello'); // 5 printLen([1, 2, 3]); // 3 printLen({ length: 10 }); // 10 printLen(123); // Invalid ``` En intressant funktion hos generics som introducerades i version 3.4 RC är typinferens för högre ordningens funktioner, vilket introducerade propagerade generiska typargument: ```typescript declare function pipe
( ab: (...args: A) => B, bc: (b: B) => C ): (...args: A) => C; declare function list(a: T): T[]; declare function box(x: V): { value: V }; const listBox = pipe(list, box); // (a: T) => { value: T[] } const boxList = pipe(box, list); // (x: V) => { value: V }[] ``` Denna funktionalitet möjliggör enklare typsäker punktfri programmering (pointfree style) vilket är vanligt inom funktionell programmering. ### Generisk kontextuell avsmalning Kontextuell avsmalning för generics är mekanismen i TypeScript som gör det möjligt för kompilatorn att smalna av typen för en generisk parameter baserat på det sammanhang där den används. Det är användbart vid arbete med generiska typer i villkorssatser: ```typescript function process(value: T): void { if (typeof value === 'string') { // Value is narrowed down to type 'string' console.log(value.length); } else if (typeof value === 'number') { // Value is narrowed down to type 'number' console.log(value.toFixed(2)); } } process('hello'); // 5 process(3.14159); // 3.14 ``` ================================================ FILE: website/src/content/docs/sv-se/book/getting-started-with-typescript.md ================================================ --- title: Komma igång med TypeScript sidebar: order: 8 label: 8. Komma igång med TypeScript --- ### Installation Visual Studio Code erbjuder utmärkt stöd för TypeScript-språket men inkluderar inte TypeScript-kompilatorn. För att installera TypeScript-kompilatorn kan du använda en pakethanterare som npm eller yarn: ```shell npm install typescript --save-dev ``` eller ```shell yarn add typescript --dev ``` Se till att committa den genererade lockfilen för att säkerställa att varje teammedlem använder samma version av TypeScript. För att köra TypeScript-kompilatorn kan du använda följande kommandon ```shell npx tsc ``` eller ```shell yarn tsc ``` Det rekommenderas att installera TypeScript projektvis snarare än globalt, eftersom det ger en mer förutsägbar byggprocess. För enstaka tillfällen kan du dock använda följande kommando: ```shell npx tsc ``` eller installera det globalt: ```shell npm install -g typescript ``` Om du använder Microsoft Visual Studio kan du hämta TypeScript som ett paket i NuGet för dina MSBuild-projekt. I NuGet Package Manager Console kör du följande kommando: ```shell Install-Package Microsoft.TypeScript.MSBuild ``` Under TypeScript-installationen installeras två körbara filer: "tsc" som TypeScript-kompilatorn och "tsserver" som den fristående TypeScript-servern. Den fristående servern innehåller kompilatorn och språktjänster som kan användas av redigerare och IDE:er för att tillhandahålla intelligent kodkomplettering. Dessutom finns det flera TypeScript-kompatibla transpilerare tillgängliga, såsom Babel (via ett plugin) eller swc. Dessa transpilerare kan användas för att konvertera TypeScript-kod till andra målspråk eller versioner. ### Konfiguration TypeScript kan konfigureras med hjälp av tsc CLI-alternativ eller genom att använda en dedikerad konfigurationsfil kallad tsconfig.json som placeras i projektets rot. För att generera en tsconfig.json-fil förfylld med rekommenderade inställningar kan du använda följande kommando: ```shell tsc --init ``` När kommandot `tsc` körs lokalt kommer TypeScript att kompilera koden med den konfiguration som anges i den närmaste tsconfig.json-filen. Här är några exempel på CLI-kommandon som körs med standardinställningarna: ```shell tsc main.ts // Compile a specific file (main.ts) to JavaScript tsc src/*.ts // Compile any .ts files under the 'src' folder to JavaScript tsc app.ts util.ts --outfile index.js // Compile two TypeScript files (app.ts and util.ts) into a single JavaScript file (index.js) ``` ### TypeScript-konfigurationsfil En tsconfig.json-fil används för att konfigurera TypeScript-kompilatorn (tsc). Vanligtvis läggs den till i projektets rot, tillsammans med filen `package.json`. Observera: * tsconfig.json accepterar kommentarer även om det är i json-format. * Det är tillrådligt att använda denna konfigurationsfil istället för kommandoradsalternativ. På följande länk hittar du den fullständiga dokumentationen och dess schema: [https://www.typescriptlang.org/tsconfig](https://www.typescriptlang.org/tsconfig) [https://www.typescriptlang.org/tsconfig/](https://www.typescriptlang.org/tsconfig/) Följande representerar en lista över de vanligaste och mest användbara konfigurationerna: #### target Egenskapen "target" används för att ange vilken version av JavaScript ECMAScript-version din TypeScript ska generera/kompilera till. För moderna webbläsare är ES6 ett bra alternativ, för äldre webbläsare rekommenderas ES5. #### lib Egenskapen "lib" används för att ange vilka biblioteksfiler som ska inkluderas vid kompilering. TypeScript inkluderar automatiskt API:er för funktioner som anges i "target"-egenskapen, men det är möjligt att utelämna eller välja specifika bibliotek för särskilda behov. Till exempel, om du arbetar med ett serverprojekt kan du exkludera "DOM"-biblioteket, som bara är användbart i en webbläsarmiljö. #### strict Egenskapen "strict" möjliggör starkare garantier och förbättrar typsäkerheten. Det är tillrådligt att alltid inkludera denna egenskap i ditt projekts tsconfig.json-fil. Att aktivera egenskapen "strict" gör att TypeScript: * Genererar kod med "use strict" för varje källfil. * Beaktar "null" och "undefined" i typkontrollprocessen. * Inaktiverar användningen av typen "any" när inga typannoteringar finns. * Ger ett fel vid användning av "this"-uttrycket, som annars skulle innebära typen "any". #### module Egenskapen "module" anger det modulsystem som stöds för det kompilerade programmet. Vid körning används en modulladdare för att lokalisera och köra beroenden baserat på det angivna modulsystemet. De vanligaste modulladdarna som används i JavaScript är Node.js CommonJS för serversidans applikationer och RequireJS för AMD-moduler i webbläsarbaserade webbapplikationer. TypeScript kan generera kod för olika modulsystem, inklusive UMD, System, ESNext, ES2015/ES6 och ES2020. Observera: Modulsystemet bör väljas baserat på målmiljön och den modulladdningsmekanism som finns tillgänglig i den miljön. #### moduleResolution Egenskapen "moduleResolution" anger strategin för modulupplösning. Använd "node" för modern TypeScript-kod, strategin "classic" används bara för gamla versioner av TypeScript (före 1.6). #### esModuleInterop Egenskapen "esModuleInterop" gör det möjligt att importera standard från CommonJS-moduler som inte exporterade med "default"-egenskapen. Denna egenskap tillhandahåller en shim för att säkerställa kompatibilitet i den genererade JavaScript-koden. Efter att ha aktiverat detta alternativ kan vi använda `import MyLibrary from "my-library"` istället för `import * as MyLibrary from "my-library"`. #### jsx Egenskapen "jsx" gäller bara för .tsx-filer som används i ReactJS och styr hur JSX-konstruktioner kompileras till JavaScript. Ett vanligt alternativ är "preserve" som kompilerar till en .jsx-fil och behåller JSX oförändrat så att det kan skickas vidare till olika verktyg som Babel för ytterligare transformationer. #### skipLibCheck Egenskapen "skipLibCheck" hindrar TypeScript från att typkontrollera hela importerade tredjepartspaket. Denna egenskap minskar kompileringstiden för ett projekt. TypeScript kommer fortfarande att kontrollera din kod mot typdefinitionerna som tillhandahålls av dessa paket. #### files Egenskapen "files" anger för kompilatorn en lista med filer som alltid måste inkluderas i programmet. #### include Egenskapen "include" anger för kompilatorn en lista med filer som vi vill inkludera. Denna egenskap tillåter glob-liknande mönster, såsom "\*_" för valfri underkatalog, "_" för valfritt filnamn och "?" för valfria tecken. #### exclude Egenskapen "exclude" anger för kompilatorn en lista med filer som inte bör inkluderas i kompileringen. Detta kan inkludera filer som "node_modules" eller testfiler. Observera: tsconfig.json tillåter kommentarer. ### importHelpers TypeScript använder hjälpkod när det genererar kod för vissa avancerade eller nedåtkompatibla JavaScript-funktioner. Som standard dupliceras dessa hjälpfunktioner i filer som använder dem. Alternativet `importHelpers` importerar dessa hjälpfunktioner från modulen `tslib` istället, vilket gör JavaScript-utdata mer effektiv. ### Råd vid migrering till TypeScript För stora projekt rekommenderas en gradvis övergång där TypeScript- och JavaScript-kod initialt samexisterar. Bara små projekt kan migreras till TypeScript på en gång. Det första steget i denna övergång är att introducera TypeScript i byggkedjeprocessen. Detta kan göras genom att använda kompilatoralternativet "allowJs", som tillåter att .ts- och .tsx-filer samexisterar med befintliga JavaScript-filer. Eftersom TypeScript faller tillbaka till typen "any" för en variabel när det inte kan härleda typen från JavaScript-filer, rekommenderas det att inaktivera "noImplicitAny" i dina kompilatoralternativ i början av migreringen. Det andra steget är att säkerställa att dina JavaScript-tester fungerar tillsammans med TypeScript-filer så att du kan köra tester allt eftersom du konverterar varje modul. Om du använder Jest, överväg att använda `ts-jest`, som gör det möjligt att testa TypeScript-projekt med Jest. Det tredje steget är att inkludera typdeklarationer för tredjepartsbibliotek i ditt projekt. Dessa deklarationer kan hittas antingen medföljande eller på DefinitelyTyped. Du kan söka efter dem med [https://www.typescriptlang.org/dt/search](https://www.typescriptlang.org/dt/search) och installera dem med: ```shell npm install --save-dev @types/package-name ``` eller ```shell yarn add --dev @types/package-name ``` Det fjärde steget är att migrera modul för modul med en nedifrån-och-upp-metod, genom att följa ditt beroendeträd med start från löven. Tanken är att börja konvertera moduler som inte är beroende av andra moduler. För att visualisera beroendegrafer kan du använda verktyget "madge". Bra kandidatmoduler för dessa initiala konverteringar är hjälpfunktioner och kod relaterad till externa API:er eller specifikationer. Det är möjligt att automatiskt generera TypeScript-typdefinitioner från Swagger-kontrakt, GraphQL- eller JSON-scheman som kan inkluderas i ditt projekt. När det inte finns några specifikationer eller officiella scheman tillgängliga kan du generera typer från rådata, såsom JSON som returneras av en server. Det rekommenderas dock att generera typer från specifikationer istället för data för att undvika att missa specialfall. Under migreringen bör du avstå från kodrefaktorisering och fokusera enbart på att lägga till typer i dina moduler. Det femte steget är att aktivera "noImplicitAny", vilket kommer att se till att alla typer är kända och definierade, vilket ger en bättre TypeScript-upplevelse för ditt projekt. Under migreringen kan du använda direktivet `@ts-check`, som aktiverar TypeScript-typkontroll i en JavaScript-fil. Detta direktiv tillhandahåller en lös version av typkontroll och kan initialt användas för att identifiera problem i JavaScript-filer. När `@ts-check` inkluderas i en fil kommer TypeScript att försöka härleda definitioner med hjälp av kommentarer i JSDoc-stil. Överväg dock att använda JSDoc-annoteringar bara i ett mycket tidigt skede av migreringen. Överväg att behålla standardvärdet för `noEmitOnError` i din tsconfig.json som false. Detta gör att du kan generera JavaScript-källkod även om fel rapporteras. ================================================ FILE: website/src/content/docs/sv-se/book/index-signatures.md ================================================ --- title: Indexsignaturer sidebar: order: 14 label: 14. Indexsignaturer --- I TypeScript kan vi använda `string`, `number` och `symbol` som indexsignatur: ```typescript type K = { [name: string | number]: string; }; const k: K = { x: 'x', 1: 'b' }; console.log(k['x']); console.log(k[1]); console.log(k['1']); // Same result as k[1] ``` Observera att JavaScript automatiskt konverterar ett index med `number` till ett index med `string`, så `k[1]` eller `k["1"]` returnerar samma värde. ================================================ FILE: website/src/content/docs/sv-se/book/infer-type-inference-in-conditional-types.md ================================================ --- title: infer-typinferens i villkorliga typer sidebar: order: 41 label: 41. infer-typinferens i villkorliga typer --- Nyckelordet `infer` används i villkorliga typer för att härleda (extrahera) typen av en generisk parameter från en typ som beror på den. Detta gör att du kan skriva mer flexibla och återanvändbara typdefinitioner. ```typescript type ElementType = T extends (infer U)[] ? U : never; type Numbers = ElementType; // number type Strings = ElementType; // string ``` ================================================ FILE: website/src/content/docs/sv-se/book/interface-and-type.md ================================================ --- title: Interface och Type sidebar: order: 48 label: 48. Interface och Type --- ### Gemensam syntax I TypeScript definierar interface strukturen hos objekt och specificerar namnen och typerna på egenskaper eller metoder som ett objekt måste ha. Den gemensamma syntaxen för att definiera ett interface i TypeScript är följande: ```typescript interface InterfaceName { property1: Type1; // ... method1(arg1: ArgType1, arg2: ArgType2): ReturnType; // ... } ``` På liknande sätt för typdefinition: ```typescript type TypeName = { property1: Type1; // ... method1(arg1: ArgType1, arg2: ArgType2): ReturnType; // ... }; ``` `interface InterfaceName` eller `type TypeName`: Definierar namnet på interfacet. `property1`: `Type1`: Specificerar interfacets egenskaper tillsammans med deras motsvarande typer. Flera egenskaper kan definieras, var och en separerad med ett semikolon. `method1(arg1: ArgType1, arg2: ArgType2): ReturnType;`: Specificerar interfacets metoder. Metoder definieras med sina namn, följt av en parameterlista inom parenteser och returtypen. Flera metoder kan definieras, var och en separerad med ett semikolon. Exempel på interface: ```typescript interface Person { name: string; age: number; greet(): void; } ``` Exempel på type: ```typescript type TypeName = { property1: string; method1(arg1: string, arg2: string): string; }; ``` I TypeScript används typer för att definiera formen på data och upprätthålla typkontroll. Det finns flera vanliga syntaxer för att definiera typer i TypeScript, beroende på det specifika användningsfallet. Här är några exempel: ### Grundläggande typer ```typescript let myNumber: number = 123; // number type let myBoolean: boolean = true; // boolean type let myArray: string[] = ['a', 'b']; // array of strings let myTuple: [string, number] = ['a', 123]; // tuple ``` ### Objekt och Interface ```typescript const x: { name: string; age: number } = { name: 'Simon', age: 7 }; ``` ### Union- och Intersection-typer ```typescript type MyType = string | number; // Union type let myUnion: MyType = 'hello'; // Can be a string myUnion = 123; // Or a number type TypeA = { name: string }; type TypeB = { age: number }; type CombinedType = TypeA & TypeB; // Intersection type let myCombined: CombinedType = { name: 'John', age: 25 }; // Object with both name and age properties ``` ================================================ FILE: website/src/content/docs/sv-se/book/intersection-types.md ================================================ --- title: Intersektionstyper sidebar: order: 32 label: 32. Intersektionstyper --- En intersektionstyp är en typ som representerar ett värde som har alla egenskaper hos två eller flera typer. Intersektionstyper betecknas med symbolen `&` mellan varje typ. ```typescript type X = { a: string; }; type Y = { b: string; }; type J = X & Y; // Intersection const j: J = { a: 'a', b: 'b', }; ``` ================================================ FILE: website/src/content/docs/sv-se/book/introduction.md ================================================ --- title: Introduktion sidebar: order: 5 label: 5. Introduktion --- Välkommen till Den koncisa TypeScript-boken! Denna guide utrustar dig med väsentlig kunskap och praktiska färdigheter för effektiv TypeScript-utveckling. Upptäck nyckelkoncept och tekniker för att skriva ren, robust kod. Oavsett om du är nybörjare eller en erfaren utvecklare fungerar denna bok både som en omfattande guide och en praktisk referens för att utnyttja TypeScripts kraft i dina projekt. Denna bok täcker TypeScript 5.2. ================================================ FILE: website/src/content/docs/sv-se/book/literal-inference.md ================================================ --- title: Literalhärledning sidebar: order: 17 label: 17. Literalhärledning --- Literalhärledning är en funktion i TypeScript som gör att typen av en variabel eller parameter kan härledas baserat på dess värde. I följande exempel kan vi se att TypeScript betraktar `x` som en literaltyp eftersom värdet inte kan ändras senare, medan `y` härleds som sträng eftersom det kan ändras när som helst. ```typescript const x = 'x'; // Literal type of 'x', because this value cannot be changed let y = 'y'; // Type string, as we can change this value ``` I följande exempel kan vi se att `o.x` härleds som en `string` (och inte en literal av `a`) eftersom TypeScript anser att värdet kan ändras när som helst. ```typescript type X = 'a' | 'b'; let o = { x: 'a', // This is a wider string }; const fn = (x: X) => `${x}-foo`; console.log(fn(o.x)); // Argument of type 'string' is not assignable to parameter of type 'X' ``` Som du kan se ger koden ett fel när `o.x` skickas till `fn` eftersom X är en smalare typ. Vi kan lösa detta problem genom att använda typbekräftelse med `const` eller typen `X`: ```typescript let o = { x: 'a' as const, }; ``` eller: ```typescript let o = { x: 'a' as X, }; ``` ================================================ FILE: website/src/content/docs/sv-se/book/literal-types.md ================================================ --- title: Literaltyper sidebar: order: 16 label: 16. Literaltyper --- En literaltyp är en enskild elementuppsättning från en kollektiv typ, den definierar ett mycket exakt värde som är en JavaScript-primitiv. Literaltyper i TypeScript är tal, strängar och booleaner. Exempel på literaler: ```typescript const a = 'a'; // String literal type const b = 1; // Numeric literal type const c = true; // Boolean literal type ``` Sträng-, numeriska och booleska literaltyper används i unioner, typvakter och typalias. I följande exempel kan du se ett unionstypealias. `O` består bara av de angivna värdena, ingen annan sträng är giltig: ```typescript type O = 'a' | 'b' | 'c'; ``` ================================================ FILE: website/src/content/docs/sv-se/book/mapped-type-modifiers.md ================================================ --- title: Modifierare för mappade typer sidebar: order: 38 label: 38. Modifierare för mappade typer --- Modifierare för mappade typer i TypeScript möjliggör transformation av egenskaper inom en befintlig typ: * `readonly` eller `+readonly`: Detta gör en egenskap i den mappade typen skrivskyddad. * `-readonly`: Detta gör att en egenskap i den mappade typen kan ändras. * `?`: Detta gör en egenskap i den mappade typen valfri. Exempel: ```typescript type ReadOnly = { readonly [P in keyof T]: T[P] }; // All properties marked as read-only type Mutable = { -readonly [P in keyof T]: T[P] }; // All properties marked as mutable type MyPartial = { [P in keyof T]?: T[P] }; // All properties marked as optional ``` ================================================ FILE: website/src/content/docs/sv-se/book/mapped-types.md ================================================ --- title: Mappade typer sidebar: order: 37 label: 37. Mappade typer --- Mappade typer i TypeScript låter dig skapa nya typer baserade på en befintlig typ genom att transformera varje egenskap med hjälp av en mappningsfunktion. Genom att mappa befintliga typer kan du skapa nya typer som representerar samma information i ett annat format. För att skapa en mappad typ kommer du åt egenskaperna hos en befintlig typ med `keyof`-operatorn och ändrar dem sedan för att producera en ny typ. I följande exempel: ```typescript type MyMappedType = { [P in keyof T]: T[P][]; }; type MyType = { foo: string; bar: number; }; type MyNewType = MyMappedType; const x: MyNewType = { foo: ['hello', 'world'], bar: [1, 2, 3], }; ``` definierar vi MyMappedType för att mappa över T:s egenskaper och skapa en ny typ där varje egenskap är en array av sin ursprungliga typ. Med hjälp av detta skapar vi MyNewType för att representera samma information som MyType, men med varje egenskap som en array. ================================================ FILE: website/src/content/docs/sv-se/book/merging-and-extension.md ================================================ --- title: Sammanslagning och utökning sidebar: order: 52 label: 52. Sammanslagning och utökning --- Sammanslagning och utökning avser två olika koncept relaterade till att arbeta med typer och interface. Sammanslagning låter dig kombinera flera deklarationer med samma namn till en enda definition, till exempel när du definierar ett interface med samma namn flera gånger: ```typescript interface X { a: string; } interface X { b: number; } const person: X = { a: 'a', b: 7, }; ``` Utökning avser möjligheten att utöka eller ärva från befintliga typer eller interface för att skapa nya. Det är en mekanism för att lägga till ytterligare egenskaper eller metoder till en befintlig typ utan att ändra dess ursprungliga definition. Exempel: ```typescript interface Animal { name: string; eat(): void; } interface Bird extends Animal { sing(): void; } const dog: Bird = { name: 'Bird 1', eat() { console.log('Eating'); }, sing() { console.log('Singing'); }, }; ``` ================================================ FILE: website/src/content/docs/sv-se/book/named-tuple-type-labeled.md ================================================ --- title: Namngiven tuppeltyp (Märkt) sidebar: order: 29 label: 29. Namngiven tuppeltyp (Märkt) --- Tuppeltyper kan inkludera valfria etiketter eller namn för varje element. Dessa etiketter är till för läsbarhet och verktygsstöd, och påverkar inte de operationer du kan utföra med dem. ```typescript type T = string; type Tuple1 = [T, T]; type Tuple2 = [a: T, b: T]; type Tuple3 = [a: T, T]; // Named Tuple plus Anonymous Tuple ``` ================================================ FILE: website/src/content/docs/sv-se/book/namespacing.md ================================================ --- title: Namnrymder sidebar: order: 57 label: 57. Namnrymder --- I TypeScript används namnrymder (namespaces) för att organisera kod i logiska behållare, förhindra namnkollisioner och ge ett sätt att gruppera relaterad kod tillsammans. Användningen av nyckelordet `export` tillåter åtkomst till namnrymden i "utomstående" moduler. ```typescript export namespace MyNamespace { export interface MyInterface1 { prop1: boolean; } export interface MyInterface2 { prop2: string; } } const a: MyNamespace.MyInterface1 = { prop1: true, }; ``` ================================================ FILE: website/src/content/docs/sv-se/book/narrowing.md ================================================ --- title: Avsmalning sidebar: order: 20 label: 20. Avsmalning --- TypeScript-avsmalning är processen att förfina typen av en variabel inom ett villkorsblock. Detta är användbart när man arbetar med unionstyper, där en variabel kan ha mer än en typ. TypeScript känner igen flera sätt att avsmalna typen: ### typeof-typvakter typeof-typvakten är en specifik typvakt i TypeScript som kontrollerar typen av en variabel baserat på dess inbyggda JavaScript-typ. ```typescript const fn = (x: number | string) => { if (typeof x === 'number') { return x + 1; // x is number } return -1; }; ``` ### Sanningsvärdesavsmalning Sanningsvärdesavsmalning i TypeScript fungerar genom att kontrollera om en variabel är sann eller falsk för att avsmalna dess typ därefter. ```typescript const toUpperCase = (name: string | null) => { if (name) { return name.toUpperCase(); } else { return null; } }; ``` ### Likhetsavsmalning Likhetsavsmalning i TypeScript fungerar genom att kontrollera om en variabel är lika med ett specifikt värde eller inte, för att avsmalma dess typ därefter. Den används tillsammans med `switch`-satser och likhetsoperatorer som `===`, `!==`, `==` och `!=` för att avsmalma typer. ```typescript const checkStatus = (status: 'success' | 'error') => { switch (status) { case 'success': return true; case 'error': return null; } }; ``` ### In-operatoravsmalning `in`-operatoravsmalning i TypeScript är ett sätt att avsmalma typen av en variabel baserat på om en egenskap finns inom variabelns typ. ```typescript type Dog = { name: string; breed: string; }; type Cat = { name: string; likesCream: boolean; }; const getAnimalType = (pet: Dog | Cat) => { if ('breed' in pet) { return 'dog'; } else { return 'cat'; } }; ``` ### instanceof-avsmalning `instanceof`-operatoravsmalning i TypeScript är ett sätt att avsmalma typen av en variabel baserat på dess konstruktorfunktion, genom att kontrollera om ett objekt är en instans av en viss klass eller gränssnitt. ```typescript class Square { constructor(public width: number) {} } class Rectangle { constructor( public width: number, public height: number ) {} } function area(shape: Square | Rectangle) { if (shape instanceof Square) { return shape.width * shape.width; } else { return shape.width * shape.height; } } const square = new Square(5); const rectangle = new Rectangle(5, 10); console.log(area(square)); // 25 console.log(area(rectangle)); // 50 ``` ================================================ FILE: website/src/content/docs/sv-se/book/never-type.md ================================================ --- title: Användning av Never-typen sidebar: order: 47 label: 47. Användning av Never-typen --- Typen `never` representerar värden som aldrig förekommer. Den används för att beteckna funktioner eller uttryck som aldrig returnerar eller kastar ett fel. Till exempel en oändlig loop: ```typescript const infiniteLoop = (): never => { while (true) { // do something } }; ``` Kasta ett fel: ```typescript const throwError = (message: string): never => { throw new Error(message); }; ``` Typen `never` är användbar för att säkerställa typsäkerhet och fånga potentiella fel i din kod. Den hjälper TypeScript att analysera och härleda mer precisa typer när den används i kombination med andra typer och kontrollflödessatser, till exempel: ```typescript type Direction = 'up' | 'down'; const move = (direction: Direction): void => { switch (direction) { case 'up': // move up break; case 'down': // move down break; default: const exhaustiveCheck: never = direction; throw new Error(`Unhandled direction: ${exhaustiveCheck}`); } }; ``` ================================================ FILE: website/src/content/docs/sv-se/book/object-types.md ================================================ --- title: Objekttyper sidebar: order: 27 label: 27. Objekttyper --- I TypeScript beskriver objekttyper formen på ett objekt. De specificerar namnen och typerna på objektets egenskaper, samt huruvida dessa egenskaper är obligatoriska eller valfria. I TypeScript kan du definiera objekttyper på två primära sätt: Interface som definierar formen på ett objekt genom att specificera namnen, typerna och valfrihet hos dess egenskaper. ```typescript interface User { name: string; age: number; email?: string; } ``` Typalias, liknande ett interface, definierar formen på ett objekt. Det kan dock även skapa en ny anpassad typ som baseras på en befintlig typ eller en kombination av befintliga typer. Detta inkluderar att definiera unionstyper, intersektionstyper och andra komplexa typer. ```typescript type Point = { x: number; y: number; }; ``` Det är också möjligt att definiera en typ anonymt: ```typescript const sum = (x: { a: number; b: number }) => x.a + x.b; console.log(sum({ a: 5, b: 1 })); ``` ================================================ FILE: website/src/content/docs/sv-se/book/optional-properties.md ================================================ --- title: Valfria egenskaper sidebar: order: 12 label: 12. Valfria egenskaper --- Ett objekt kan specificera valfria egenskaper genom att lägga till ett frågetecken `?` i slutet av egenskapsnamnet: ```typescript type X = { a: number; b?: number; // Optional }; ``` Det är möjligt att ange ett standardvärde när en egenskap är valfri: ```typescript type X = { a: number; b?: number; }; const x = ({ a, b = 100 }: X) => a + b; ``` ================================================ FILE: website/src/content/docs/sv-se/book/others.md ================================================ --- title: Övrigt sidebar: order: 61 label: 61. Övrigt --- ### Fel och undantagshantering TypeScript låter dig fånga och hantera fel med hjälp av JavaScripts standardmekanismer för felhantering: Try-Catch-Finally-block: ```typescript try { // Code that might throw an error } catch (error) { // Handle the error } finally { // Code that always executes, finally is optional } ``` Du kan också hantera olika typer av fel: ```typescript try { // Code that might throw different types of errors } catch (error) { if (error instanceof TypeError) { // Handle TypeError } else if (error instanceof RangeError) { // Handle RangeError } else { // Handle other errors } } ``` Anpassade feltyper: Det är möjligt att specificera mer specifika fel genom att utöka `Error`-klassen: ```typescript class CustomError extends Error { constructor(message: string) { super(message); this.name = 'CustomError'; } } throw new CustomError('This is a custom error.'); ``` ### Mixin-klasser Mixin-klasser låter dig kombinera och komponera beteende från flera klasser till en enda klass. De erbjuder ett sätt att återanvända och utöka funktionalitet utan behov av djupa arvskedjor. ```typescript abstract class Identifiable { name: string = ''; logId() { console.log('id:', this.name); } } abstract class Selectable { selected: boolean = false; select() { this.selected = true; console.log('Select'); } deselect() { this.selected = false; console.log('Deselect'); } } class MyClass { constructor() {} } // Extend MyClass to include the behavior of Identifiable and Selectable interface MyClass extends Identifiable, Selectable {} // Function to apply mixins to a class function applyMixins(source: any, baseCtors: any[]) { baseCtors.forEach(baseCtor => { Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => { let descriptor = Object.getOwnPropertyDescriptor( baseCtor.prototype, name ); if (descriptor) { Object.defineProperty(source.prototype, name, descriptor); } }); }); } // Apply the mixins to MyClass applyMixins(MyClass, [Identifiable, Selectable]); let o = new MyClass(); o.name = 'abc'; o.logId(); o.select(); ``` ### Asynkrona språkfunktioner Eftersom TypeScript är en superset av JavaScript har det inbyggda asynkrona språkfunktioner från JavaScript som: Promises: Promises är ett sätt att hantera asynkrona operationer och deras resultat med hjälp av metoder som `.then()` och `.catch()` för att hantera framgångs- och feltillstånd. Läs mer: [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) Async/await: Nyckelorden async/await erbjuder en mer synkront utseende syntax för att arbeta med Promises. Nyckelordet `async` används för att definiera en asynkron funktion, och nyckelordet `await` används inom en async-funktion för att pausa körningen tills ett Promise har lösts eller avvisats. Läs mer: [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function) [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await) Följande API:er stöds väl i TypeScript: Fetch API: [https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) Web Workers: [https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API) Shared Workers: [https://developer.mozilla.org/en-US/docs/Web/API/SharedWorker](https://developer.mozilla.org/en-US/docs/Web/API/SharedWorker) WebSocket: [https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API) ### Iteratorer och generatorer Både iteratorer och generatorer stöds väl i TypeScript. Iteratorer är objekt som implementerar iteratorprotokollet och erbjuder ett sätt att komma åt element i en samling eller sekvens ett i taget. Det är en struktur som innehåller en pekare till nästa element i iterationen. De har en `next()`-metod som returnerar nästa värde i sekvensen tillsammans med en boolean som anger om sekvensen är `done` (klar). ```typescript class NumberIterator implements Iterable { private current: number; constructor( private start: number, private end: number ) { this.current = start; } public next(): IteratorResult { if (this.current <= this.end) { const value = this.current; this.current++; return { value, done: false }; } else { return { value: undefined, done: true }; } } [Symbol.iterator](): Iterator { return this; } } const iterator = new NumberIterator(1, 3); for (const num of iterator) { console.log(num); } ``` Generatorer är speciella funktioner definierade med syntaxen `function*` som förenklar skapandet av iteratorer. De använder nyckelordet `yield` för att definiera sekvensen av värden och pausar och återupptar automatiskt körningen när värden efterfrågas. Generatorer gör det enklare att skapa iteratorer och är särskilt användbara för att arbeta med stora eller oändliga sekvenser. Exempel: ```typescript function* numberGenerator(start: number, end: number): Generator { for (let i = start; i <= end; i++) { yield i; } } const generator = numberGenerator(1, 5); for (const num of generator) { console.log(num); } ``` TypeScript stöder också asynkrona iteratorer och asynkrona generatorer. Läs mer: [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Generator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Generator) [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Iterator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Iterator) ### TsDocs JSDoc-referens När man arbetar med en JavaScript-kodbas är det möjligt att hjälpa TypeScript att härleda rätt typ genom att använda JSDoc-kommentarer med ytterligare annotationer för att ge typinformation. Exempel: ```typescript /** * Computes the power of a given number * @constructor * @param {number} base – The base value of the expression * @param {number} exponent – The exponent value of the expression */ function power(base: number, exponent: number) { return Math.pow(base, exponent); } power(10, 2); // function power(base: number, exponent: number): number ``` Fullständig dokumentation finns på denna länk: [https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html](https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html) Från version 3.7 är det möjligt att generera .d.ts-typdefinitioner från JavaScript JSDoc-syntax. Mer information finns här: [https://www.typescriptlang.org/docs/handbook/declaration-files/dts-from-js.html](https://www.typescriptlang.org/docs/handbook/declaration-files/dts-from-js.html) ### @types Paket under @types-organisationen är speciella paketnamnkonventioner som används för att tillhandahålla typdefinitioner för befintliga JavaScript-bibliotek eller moduler. Till exempel genom att använda: ```shell npm install --save-dev @types/lodash ``` Installeras typdefinitionerna för `lodash` i ditt nuvarande projekt. För att bidra till typdefinitionerna för @types-paket, skicka in en pull request till [https://github.com/DefinitelyTyped/DefinitelyTyped](https://github.com/DefinitelyTyped/DefinitelyTyped). ### JSX JSX (JavaScript XML) är en utökning av JavaScript-språksyntaxen som låter dig skriva HTML-liknande kod i dina JavaScript- eller TypeScript-filer. Det används vanligtvis i React för att definiera HTML-strukturen. TypeScript utökar JSX:s kapacitet genom att tillhandahålla typkontroll och statisk analys. För att använda JSX behöver du ställa in kompileringsalternativet `jsx` i din `tsconfig.json`-fil. Två vanliga konfigurationsalternativ: * "preserve": genererar .jsx-filer med JSX oförändrad. Detta alternativ talar om för TypeScript att behålla JSX-syntaxen som den är och inte transformera den under kompileringsprocessen. Du kan använda detta alternativ om du har ett separat verktyg, som Babel, som hanterar transformationen. * "react": aktiverar TypeScripts inbyggda JSX-transformation. React.createElement kommer att användas. Alla alternativ finns tillgängliga här: [https://www.typescriptlang.org/tsconfig#jsx](https://www.typescriptlang.org/tsconfig#jsx) ### ES6-moduler TypeScript stöder ES6 (ECMAScript 2015) och många efterföljande versioner. Det innebär att du kan använda ES6-syntax, såsom pilfunktioner, mallsträngar, klasser, moduler, destrukturering och mer. För att aktivera ES6-funktioner i ditt projekt kan du ange egenskapen `target` i tsconfig.json. Ett konfigurationsexempel: ```json { "compilerOptions": { "target": "es6", "module": "es6", "moduleResolution": "node", "sourceMap": true, "outDir": "dist" }, "include": ["src"] } ``` ### ES7 exponentiationsoperator Exponentiationsoperatorn (`**`) beräknar värdet som erhålls genom att upphöja den första operanden till den andra operandens potens. Den fungerar på liknande sätt som `Math.pow()`, men med den ytterligare förmågan att acceptera BigInts som operander. TypeScript stöder denna operator fullt ut genom att använda `es2016` eller en senare version som `target` i din tsconfig.json-fil. ```typescript console.log(2 ** (2 ** 2)); // 16 ``` ### for-await-of-satsen Detta är en JavaScript-funktion som stöds fullt ut i TypeScript och som låter dig iterera över asynkrona iterbara objekt från målversion es2018. ```typescript async function* asyncNumbers(): AsyncIterableIterator { yield Promise.resolve(1); yield Promise.resolve(2); yield Promise.resolve(3); } (async () => { for await (const num of asyncNumbers()) { console.log(num); } })(); ``` ### Metaegenskapen new.target Du kan i TypeScript använda metaegenskapen `new.target` som gör det möjligt att avgöra om en funktion eller konstruktor anropades med new-operatorn. Det låter dig upptäcka om ett objekt skapades som ett resultat av ett konstruktoranrop. ```typescript class Parent { constructor() { console.log(new.target); // Logs the constructor function used to create an instance } } class Child extends Parent { constructor() { super(); } } const parentX = new Parent(); // [Function: Parent] const child = new Child(); // [Function: Child] ``` ### Dynamiska importuttryck Det är möjligt att villkorligt ladda moduler eller lat-ladda dem vid behov med hjälp av ECMAScript-förslaget för dynamisk import som stöds i TypeScript. Syntaxen för dynamiska importuttryck i TypeScript är följande: ```typescript async function renderWidget() { const container = document.getElementById('widget'); if (container !== null) { const widget = await import('./widget'); // Dynamic import widget.render(container); } } renderWidget(); ``` ### "tsc –watch" Detta kommando startar TypeScript-kompilatorn med parametern `--watch`, med möjligheten att automatiskt kompilera om TypeScript-filer när de ändras. ```shell tsc --watch ``` Från och med TypeScript version 4.9 förlitar sig filövervakning främst på filsystemhändelser och faller automatiskt tillbaka till polling om en händelsebaserad övervakare inte kan upprättas. ### Non-null Assertion Operator Non-null Assertion Operator (Postfix !) även kallad Definite Assignment Assertions är en TypeScript-funktion som låter dig försäkra att en variabel eller egenskap inte är null eller undefined, även om TypeScripts statiska typanalys antyder att den kan vara det. Med denna funktion är det möjligt att ta bort all explicit kontroll. ```typescript type Person = { name: string; }; const printName = (person?: Person) => { console.log(`Name is ${person!.name}`); }; ``` ### Standarddeklarationer Standarddeklarationer används när en variabel eller parameter tilldelas ett standardvärde. Det innebär att om inget värde anges för den variabeln eller parametern, kommer standardvärdet att användas istället. ```typescript function greet(name: string = 'Anonymous'): void { console.log(`Hello, ${name}!`); } greet(); // Hello, Anonymous! greet('John'); // Hello, John! ``` ### Valfri kedjning (Optional Chaining) Den valfria kedjningsoperatorn `?.` fungerar som den vanliga punktoperatorn (`.`) för att komma åt egenskaper eller metoder. Den hanterar dock null- eller undefined-värden smidigt genom att avsluta uttrycket och returnera `undefined`, istället för att kasta ett fel. ```typescript type Person = { name: string; age?: number; address?: { street?: string; city?: string; }; }; const person: Person = { name: 'John', }; console.log(person.address?.city); // undefined ``` ### Nullish coalescing-operatorn Nullish coalescing-operatorn `??` returnerar högerledet om vänsterledet är `null` eller `undefined`; annars returnerar den vänsterledets värde. ```typescript const foo = null ?? 'foo'; console.log(foo); // foo const baz = 1 ?? 'baz'; const baz2 = 0 ?? 'baz'; console.log(baz); // 1 console.log(baz2); // 0 ``` ### Mallsträngslitteraltyper (Template Literal Types) Mallsträngslitteraltyper gör det möjligt att manipulera strängvärden på typnivå och generera nya strängtyper baserade på befintliga. De är användbara för att skapa mer uttrycksfulla och precisa typer från strängbaserade operationer. ```typescript type Department = 'engineering' | 'hr'; type Language = 'english' | 'spanish'; type Id = `${Department}-${Language}-id`; // "engineering-english-id" | "engineering-spanish-id" | "hr-english-id" | "hr-spanish-id" ``` ### Funktionsöverlagring Funktionsöverlagring låter dig definiera flera funktionssignaturer för samma funktionsnamn, var och en med olika parametertyper och returtyp. När du anropar en överlagrad funktion använder TypeScript de angivna argumenten för att avgöra rätt funktionssignatur: ```typescript function makeGreeting(name: string): string; function makeGreeting(names: string[]): string[]; function makeGreeting(person: unknown): unknown { if (typeof person === 'string') { return `Hi ${person}!`; } else if (Array.isArray(person)) { return person.map(name => `Hi, ${name}!`); } throw new Error('Unable to greet'); } makeGreeting('Simon'); makeGreeting(['Simone', 'John']); ``` ### Rekursiva typer En rekursiv typ är en typ som kan referera till sig själv. Detta är användbart för att definiera datastrukturer som har en hierarkisk eller rekursiv struktur (potentiellt oändlig nästning), såsom länkade listor, träd och grafer. ```typescript type ListNode = { data: T; next: ListNode | undefined; }; ``` ### Rekursiva villkorstyper Det är möjligt att definiera komplexa typrelationer med hjälp av logik och rekursion i TypeScript. Låt oss bryta ner det i enkla termer: Villkorstyper: låter dig definiera typer baserade på booleska villkor: ```typescript type CheckNumber = T extends number ? 'Number' : 'Not a number'; type A = CheckNumber<123>; // 'Number' type B = CheckNumber<'abc'>; // 'Not a number' ``` Rekursion: innebär en typdefinition som refererar till sig själv inom sin egen definition: ```typescript type Json = string | number | boolean | null | Json[] | { [key: string]: Json }; const data: Json = { prop1: true, prop2: 'prop2', prop3: { prop4: [], }, }; ``` Rekursiva villkorstyper kombinerar både villkorslogik och rekursion. Det innebär att en typdefinition kan bero på sig själv genom villkorslogik, vilket skapar komplexa och flexibla typrelationer. ```typescript type Flatten = T extends Array ? Flatten : T; type NestedArray = [1, [2, [3, 4], 5], 6]; type FlattenedArray = Flatten; // 2 | 3 | 4 | 5 | 1 | 6 ``` ### Stöd för ECMAScript-moduler i Node Node.js lade till stöd för ECMAScript-moduler från och med version 15.3.0, och TypeScript har haft stöd för ECMAScript-moduler i Node.js sedan version 4.7. Detta stöd kan aktiveras genom att använda egenskapen `module` med värdet `nodenext` i tsconfig.json-filen. Här är ett exempel: ```json { "compilerOptions": { "module": "nodenext", "outDir": "./lib", "declaration": true } } ``` Node.js stöder två filändelser för moduler: `.mjs` för ES-moduler och `.cjs` för CommonJS-moduler. Motsvarande filändelser i TypeScript är `.mts` för ES-moduler och `.cts` för CommonJS-moduler. När TypeScript-kompilatorn transpilerar dessa filer till JavaScript skapar den `.mjs`- och `.cjs`-filer. Om du vill använda ES-moduler i ditt projekt kan du ställa in egenskapen `type` till "module" i din package.json-fil. Detta instruerar Node.js att behandla projektet som ett ES-modulprojekt. Dessutom stöder TypeScript även typdeklarationer i .d.ts-filer. Dessa deklarationsfiler tillhandahåller typinformation för bibliotek eller moduler skrivna i TypeScript, vilket låter andra utvecklare använda dem med TypeScripts typkontroll och autokompletteringsfunktioner. ### Assertionsfunktioner I TypeScript är assertionsfunktioner funktioner som indikerar verifieringen av ett specifikt villkor baserat på sitt returvärde. I sin enklaste form undersöker en assert-funktion ett givet predikat och kastar ett fel när predikatet utvärderas till false. ```typescript function isNumber(value: unknown): asserts value is number { if (typeof value !== 'number') { throw new Error('Not a number'); } } ``` De kan också deklareras som funktionsuttryck: ```typescript type AssertIsNumber = (value: unknown) => asserts value is number; const isNumber: AssertIsNumber = value => { if (typeof value !== 'number') { throw new Error('Not a number'); } }; ``` Assertionsfunktioner delar likheter med typskydd (type guards). Typskydd introducerades ursprungligen för att utföra körtidskontroller och säkerställa typen av ett värde inom ett specifikt scope. Specifikt är ett typskydd en funktion som utvärderar ett typpredikat och returnerar ett booleskt värde som anger om predikatet är sant eller falskt. Detta skiljer sig något från assertionsfunktioner, där avsikten är att kasta ett fel snarare än att returnera false när predikatet inte uppfylls. Exempel på typskydd: ```typescript const isNumber = (value: unknown): value is number => typeof value === 'number'; ``` ### Variadiska tuppeltyper Variadiska tuppeltyper är en funktion som introducerades i TypeScript version 4.0. Låt oss börja med att repetera vad en tuppel är: En tuppeltyp är en array som har en definierad längd, och där typen av varje element är känd: ```typescript type Student = [string, number]; const [name, age]: Student = ['Simone', 20]; ``` Termen "variadisk" betyder obestämd aritet (accepterar ett variabelt antal argument). En variadisk tuppel är en tuppeltyp som har alla egenskaper som ovan men vars exakta form ännu inte är definierad: ```typescript type Bar = [boolean, ...T, number]; type A = Bar<[boolean]>; // [boolean, boolean, number] type B = Bar<['a', 'b']>; // [boolean, 'a', 'b', number] type C = Bar<[]>; // [boolean, number] ``` I den föregående koden kan vi se att tuppelns form definieras av den generiska typen `T` som skickas in. Variadiska tuppler kan acceptera flera generiska typer vilket gör dem mycket flexibla: ```typescript type Bar = [...T, boolean, ...G]; type A = Bar<[number], [string]>; // [number, boolean, string] type B = Bar<['a', 'b'], [boolean]>; // ["a", "b", boolean, boolean] ``` Med de nya variadiska tupplerna kan vi använda: * Spridningar i tuppeltypssyntax kan nu vara generiska, så vi kan representera högre ordningens operationer på tuppler och arrayer även när vi inte känner till de faktiska typerna vi opererar på. * Restelement kan förekomma var som helst i en tuppel. Exempel: ```typescript type Items = readonly unknown[]; function concat( arr1: T, arr2: U ): [...T, ...U] { return [...arr1, ...arr2]; } concat([1, 2, 3], ['4', '5', '6']); // [1, 2, 3, "4", "5", "6"] ``` ### Inkapslingstyper (Boxed types) Inkapslingstyper refererar till omslagsobjekten som används för att representera primitiva typer som objekt. Dessa omslagsobjekt tillhandahåller ytterligare funktionalitet och metoder som inte är direkt tillgängliga på de primitiva värdena. När du anropar en metod som `charAt` eller `normalize` på en primitiv `string`, omsluter JavaScript den i ett `String`-objekt, anropar metoden och kastar sedan bort objektet. Demonstration: ```typescript const originalNormalize = String.prototype.normalize; String.prototype.normalize = function () { console.log(this, typeof this); return originalNormalize.call(this); }; console.log('\u0041'.normalize()); ``` TypeScript representerar denna skillnad genom att tillhandahålla separata typer för primitiverna och deras motsvarande objektomslag: * string => String * number => Number * boolean => Boolean * symbol => Symbol * bigint => BigInt Inkapslingstyperna behövs vanligtvis inte. Undvik att använda inkapslingstyper och använd istället typer för primitiverna, till exempel `string` istället för `String`. ### Kovarians och kontravarians i TypeScript Kovarians och kontravarians används för att beskriva hur relationer fungerar vid hantering av arv eller tilldelning av typer. Kovarians innebär att en typrelation bevarar riktningen för arv eller tilldelning, så om en typ A är en subtyp av typ B, anses också en array av typ A vara en subtyp av en array av typ B. Det viktiga att notera här är att subtyprelationen bibehålls, vilket innebär att kovarians accepterar subtyper men inte accepterar supertyper. Kontravarians innebär att en typrelation vänder riktningen för arv eller tilldelning, så om en typ A är en subtyp av typ B, anses en array av typ B vara en subtyp av en array av typ A. Subtyprelationen är omvänd, vilket innebär att kontravarians accepterar supertyper men inte accepterar subtyper. Notera: Bivarians innebär att både supertyper och subtyper accepteras. Exempel: Låt oss säga att vi har ett utrymme för alla djur och ett separat utrymme bara för hundar. Vid kovarians kan du placera alla hundar i djurutrymmet eftersom hundar är en typ av djur. Men du kan inte placera alla djur i hundutrymmet eftersom det kan finnas andra djur inblandade. Vid kontravarians kan du inte placera alla djur i hundutrymmet eftersom djurutrymmet kan innehålla andra djur också. Däremot kan du placera alla hundar i djurutrymmet eftersom alla hundar också är djur. ```typescript // Covariance example class Animal { name: string; constructor(name: string) { this.name = name; } } class Dog extends Animal { breed: string; constructor(name: string, breed: string) { super(name); this.breed = breed; } } let animals: Animal[] = []; let dogs: Dog[] = []; // Covariance allows assigning subtype (Dog) array to supertype (Animal) array animals = dogs; dogs = animals; // Invalid: Type 'Animal[]' is not assignable to type 'Dog[]' // Contravariance example type Feed = (animal: T) => void; let feedAnimal: Feed = (animal: Animal) => { console.log(`Animal name: ${animal.name}`); }; let feedDog: Feed = (dog: Dog) => { console.log(`Dog name: ${dog.name}, Breed: ${dog.breed}`); }; // Contravariance allows assigning supertype (Animal) callback to subtype (Dog) callback feedDog = feedAnimal; feedAnimal = feedDog; // Invalid: Type 'Feed' is not assignable to type 'Feed'. ``` I TypeScript är typrelationer för arrayer kovarianta, medan typrelationer för funktionsparametrar är kontravarianta. Det innebär att TypeScript uppvisar både kovarians och kontravarians beroende på sammanhanget. #### Valfria variansannotationer för typparametrar Från och med TypeScript 4.7.0 kan vi använda nyckelorden `out` och `in` för att vara specifika med variansannotationer. För kovarians, använd nyckelordet `out`: ```typescript type AnimalCallback = () => T; // T is Covariant here ``` Och för kontravarians, använd nyckelordet `in`: ```typescript type AnimalCallback = (value: T) => void; // T is Contravariance here ``` ### Mallsträngsmönsterindexsignaturer Mallsträngsmönsterindexsignaturer gör det möjligt att definiera flexibla indexsignaturer med hjälp av mallsträngsmönster. Denna funktion gör det möjligt att skapa objekt som kan indexeras med specifika mönster av strängnycklar, vilket ger mer kontroll och specificitet vid åtkomst och manipulering av egenskaper. TypeScript tillåter från version 4.4 indexsignaturer för symboler och mallsträngsmönster. ```typescript const uniqueSymbol = Symbol('description'); type MyKeys = `key-${string}`; type MyObject = { [uniqueSymbol]: string; [key: MyKeys]: number; }; const obj: MyObject = { [uniqueSymbol]: 'Unique symbol key', 'key-a': 123, 'key-b': 456, }; console.log(obj[uniqueSymbol]); // Unique symbol key console.log(obj['key-a']); // 123 console.log(obj['key-b']); // 456 ``` ### satisfies-operatorn Operatorn `satisfies` låter dig kontrollera om en given typ uppfyller ett specifikt gränssnitt eller villkor. Med andra ord säkerställer den att en typ har alla nödvändiga egenskaper och metoder för ett specifikt gränssnitt. Det är ett sätt att säkerställa att en variabel passar in i en typdefinition. Här är ett exempel: ```typescript type Columns = 'name' | 'nickName' | 'attributes'; type User = Record; // Type Annotation using `User` const user: User = { name: 'Simone', nickName: undefined, attributes: ['dev', 'admin'], }; // In the following lines, TypeScript won't be able to infer properly user.attributes?.map(console.log); // Property 'map' does not exist on type 'string | string[]'. Property 'map' does not exist on type 'string'. user.nickName; // string | string[] | undefined // Type assertion using `as` const user2 = { name: 'Simon', nickName: undefined, attributes: ['dev', 'admin'], } as User; // Here too, TypeScript won't be able to infer properly user2.attributes?.map(console.log); // Property 'map' does not exist on type 'string | string[]'. Property 'map' does not exist on type 'string'. user2.nickName; // string | string[] | undefined // Using `satisfies` operators we can properly infer the types now const user3 = { name: 'Simon', nickName: undefined, attributes: ['dev', 'admin'], } satisfies User; user3.attributes?.map(console.log); // TypeScript infers correctly: string[] user3.nickName; // TypeScript infers correctly: undefined ``` ### Importer och exporter av enbart typer Importer och exporter av enbart typer låter dig importera eller exportera typer utan att importera eller exportera de värden eller funktioner som är associerade med dessa typer. Detta kan vara användbart för att minska storleken på ditt paket. För att använda importer av enbart typer kan du använda nyckelordet `import type`. TypeScript tillåter användning av både deklarations- och implementationsfiländelser (.ts, .mts, .cts och .tsx) i importer av enbart typer, oavsett inställningarna för `allowImportingTsExtensions`. Till exempel: ```typescript import type { House } from './house.ts'; ``` Följande former stöds: ```typescript import type T from './mod'; import type { A, B } from './mod'; import type * as Types from './mod'; export type { T }; export type { T } from './mod'; ``` ### using-deklaration och explicit resurshantering En `using`-deklaration är en blockomfattande, oföränderlig bindning, liknande `const`, som används för att hantera disponibla resurser. När den initialiseras med ett värde registreras värdets `Symbol.dispose`-metod och körs sedan vid utgång ur det omslutande blockomfånget. Detta baseras på ECMAScripts resurshanteringsfunktion, som är användbar för att utföra väsentliga uppstädningsuppgifter efter objektskapande, såsom att stänga anslutningar, radera filer och frigöra minne. Noteringar: * På grund av dess nyliga introduktion i TypeScript version 5.2 saknar de flesta körmiljöer inbyggt stöd. Du behöver polyfills för: `Symbol.dispose`, `Symbol.asyncDispose`, `DisposableStack`, `AsyncDisposableStack`, `SuppressedError`. * Dessutom behöver du konfigurera din tsconfig.json enligt följande: ```json { "compilerOptions": { "target": "es2022", "lib": ["es2022", "esnext.disposable", "dom"] } } ``` Exempel: ```typescript //@ts-ignore Symbol.dispose ??= Symbol('Symbol.dispose'); // Simple polify const doWork = (): Disposable => { return { [Symbol.dispose]: () => { console.log('disposed'); }, }; }; console.log(1); { using work = doWork(); // Resource is declared console.log(2); } // Resource is disposed (e.g., `work[Symbol.dispose]()` is evaluated) console.log(3); ``` Koden loggar: ```shell 1 2 disposed 3 ``` En resurs som kan disponeras måste följa `Disposable`-gränssnittet: ```typescript // lib.esnext.disposable.d.ts interface Disposable { [Symbol.dispose](): void; } ``` `using`-deklarationer registrerar resursavyttringsoperationer i en stack, vilket säkerställer att de disponeras i omvänd ordning mot deklarationen: ```typescript { using j = getA(), y = getB(); using k = getC(); } // disposes `C`, then `B`, then `A`. ``` Resurser garanteras att disponeras, även om efterföljande kod eller undantag uppstår. Detta kan leda till att avyttringen potentiellt kastar ett undantag, som eventuellt undertrycker ett annat. För att behålla information om undertryckta fel introduceras ett nytt inbyggt undantag, `SuppressedError`. #### await using-deklaration En `await using`-deklaration hanterar en asynkront disponibel resurs. Värdet måste ha en `Symbol.asyncDispose`-metod, som kommer att inväntas vid blockets slut. ```typescript async function doWorkAsync() { await using work = doWorkAsync(); // Resource is declared } // Resource is disposed (e.g., `await work[Symbol.asyncDispose]()` is evaluated) ``` För en asynkront disponibel resurs måste den följa antingen `Disposable`- eller `AsyncDisposable`-gränssnittet: ```typescript // lib.esnext.disposable.d.ts interface AsyncDisposable { [Symbol.asyncDispose](): Promise; } ``` ```typescript //@ts-ignore Symbol.asyncDispose ??= Symbol('Symbol.asyncDispose'); // Simple polify class DatabaseConnection implements AsyncDisposable { // A method that is called when the object is disposed asynchronously [Symbol.asyncDispose]() { // Close the connection and return a promise return this.close(); } async close() { console.log('Closing the connection...'); await new Promise(resolve => setTimeout(resolve, 1000)); console.log('Connection closed.'); } } async function doWork() { // Create a new connection and dispose it asynchronously when it goes out of scope await using connection = new DatabaseConnection(); // Resource is declared console.log('Doing some work...'); } // Resource is disposed (e.g., `await connection[Symbol.asyncDispose]()` is evaluated) doWork(); ``` Koden loggar: ```shell Doing some work... Closing the connection... Connection closed. ``` Deklarationerna `using` och `await using` är tillåtna i följande satser: `for`, `for-in`, `for-of`, `for-await-of`, `switch`. ### Importattribut TypeScript 5.3:s importattribut (etiketter för importer) talar om för körmiljön hur moduler (JSON, etc.) ska hanteras. Detta förbättrar säkerheten genom att säkerställa tydliga importer och överensstämmer med Content Security Policy (CSP) för säkrare resursladdning. TypeScript säkerställer att de är giltiga men låter körmiljön hantera deras tolkning för specifik modulhantering. Exempel: ```typescript import config from './config.json' with { type: 'json' }; ``` med dynamisk import: ```typescript const config = import('./config.json', { with: { type: 'json' } }); ``` ================================================ FILE: website/src/content/docs/sv-se/book/overloads.md ================================================ --- title: Överlagringar sidebar: order: 51 label: 51. Överlagringar --- Funktionsöverlagringar i TypeScript låter dig definiera flera funktionssignaturer för ett enda funktionsnamn, vilket gör det möjligt att definiera funktioner som kan anropas på flera sätt. Här är ett exempel: ```typescript // Overloads function sayHi(name: string): string; function sayHi(names: string[]): string[]; // Implementation function sayHi(name: unknown): unknown { if (typeof name === 'string') { return `Hi, ${name}!`; } else if (Array.isArray(name)) { return name.map(name => `Hi, ${name}!`); } throw new Error('Invalid value'); } sayHi('xx'); // Valid sayHi(['aa', 'bb']); // Valid ``` Här är ytterligare ett exempel på användning av funktionsöverlagringar inom en `class`: ```typescript class Greeter { message: string; constructor(message: string) { this.message = message; } // overload sayHi(name: string): string; sayHi(names: string[]): ReadonlyArray; // implementation sayHi(name: unknown): unknown { if (typeof name === 'string') { return `${this.message}, ${name}!`; } else if (Array.isArray(name)) { return name.map(name => `${this.message}, ${name}!`); } throw new Error('value is invalid'); } } console.log(new Greeter('Hello').sayHi('Simon')); ``` ================================================ FILE: website/src/content/docs/sv-se/book/predefined-conditional-types.md ================================================ --- title: Fördefinierade villkorliga typer sidebar: order: 42 label: 42. Fördefinierade villkorliga typer --- I TypeScript är fördefinierade villkorliga typer inbyggda villkorliga typer som tillhandahålls av språket. De är utformade för att utföra vanliga typtransformationer baserade på egenskaperna hos en given typ. `Exclude`: Denna typ tar bort alla typer från Type som kan tilldelas till ExcludedType. `Extract`: Denna typ extraherar alla typer från Union som kan tilldelas till Type. `NonNullable`: Denna typ tar bort null och undefined från Type. `ReturnType`: Denna typ extraherar returtypen av en funktionstyp Type. `Parameters`: Denna typ extraherar parametertyperna av en funktionstyp Type. `Required`: Denna typ gör alla egenskaper i Type obligatoriska. `Partial`: Denna typ gör alla egenskaper i Type valfria. `Readonly`: Denna typ gör alla egenskaper i Type skrivskyddade. ================================================ FILE: website/src/content/docs/sv-se/book/primitive-types.md ================================================ --- title: Primitiva typer sidebar: order: 10 label: 10. Primitiva typer --- TypeScript stöder 7 primitiva typer. En primitiv datatyp avser en typ som inte är ett objekt och som inte har några metoder kopplade till sig. I TypeScript är alla primitiva typer oföränderliga, vilket innebär att deras värden inte kan ändras efter att de har tilldelats. ### string Den primitiva typen `string` lagrar textdata, och värdet omges alltid av dubbla eller enkla citattecken. ```typescript const x: string = 'x'; const y: string = 'y'; ``` Strängar kan sträcka sig över flera rader om de omges av backtick-tecknet (`): ```typescript let sentence: string = `xxx, yyy`; ``` ### boolean Datatypen `boolean` i TypeScript lagrar ett binärt värde, antingen `true` eller `false`. ```typescript const isReady: boolean = true; ``` ### number En `number`-datatyp i TypeScript representeras med ett 64-bitars flyttalsvärde. En `number`-typ kan representera heltal och bråktal. TypeScript stöder även hexadecimala, binära och oktala tal, till exempel: ```typescript const decimal: number = 10; const hexadecimal: number = 0xa00d; // Hexadecimal starts with 0x const binary: number = 0b1010; // Binary starts with 0b const octal: number = 0o633; // Octal starts with 0o ``` ### bigInt En `bigInt` representerar numeriska värden som är mycket stora (253 – 1) och inte kan representeras med en `number`. En `bigInt` kan skapas genom att anropa den inbyggda funktionen `BigInt()` eller genom att lägga till `n` i slutet av ett heltalsliteral: ```typescript const x: bigint = BigInt(9007199254740991); const y: bigint = 9007199254740991n; ``` Noteringar: * `bigInt`-värden kan inte blandas med `number` och kan inte användas med inbyggd `Math`, de måste konverteras till samma typ. * `bigInt`-värden är bara tillgängliga om målkonfigurationen är ES2020 eller högre. ### Symbol Symboler är unika identifierare som kan användas som egenskapsnycklar i objekt för att förhindra namnkonflikter. ```typescript type Obj = { [sym: symbol]: number; }; const a = Symbol('a'); const b = Symbol('b'); let obj: Obj = {}; obj[a] = 123; obj[b] = 456; console.log(obj[a]); // 123 console.log(obj[b]); // 456 ``` ### null och undefined `null` och `undefined`-typerna representerar båda inget värde eller frånvaron av något värde. Typen `undefined` betyder att värdet inte är tilldelat eller initierat och indikerar en oavsiktlig frånvaro av värde. Typen `null` betyder att vi vet att fältet inte har något värde, alltså är värdet otillgängligt, och det indikerar en avsiktlig frånvaro av värde. ### Array En `array` är en datatyp som kan lagra flera värden av samma typ eller inte. Den kan definieras med följande syntax: ```typescript const x: string[] = ['a', 'b']; const y: Array = ['a', 'b']; const j: Array = ['a', 1, 'b', 2]; // Union ``` TypeScript stöder skrivskyddade arrayer med följande syntax: ```typescript const x: readonly string[] = ['a', 'b']; // Readonly modifier const y: ReadonlyArray = ['a', 'b']; const j: ReadonlyArray = ['a', 1, 'b', 2]; j.push('x'); // Invalid ``` TypeScript stöder tupler och skrivskyddade tupler: ```typescript const x: [string, number] = ['a', 1]; const y: readonly [string, number] = ['a', 1]; ``` ### any Datatypen `any` representerar bokstavligen "vilket som helst" värde, det är standardvärdet när TypeScript inte kan härleda typen eller den inte är specificerad. När `any` används hoppar TypeScript-kompilatorn över typkontrollen, så det finns ingen typsäkerhet när `any` används. Använd i allmänhet inte `any` för att tysta kompilatorn när ett fel uppstår, fokusera istället på att åtgärda felet eftersom det med `any` är möjligt att bryta kontrakt och vi förlorar fördelarna med TypeScripts autokomplettering. Typen `any` kan vara användbar vid en gradvis migrering från JavaScript till TypeScript, eftersom den kan tysta kompilatorn. För nya projekt, använd TypeScript-konfigurationen `noImplicitAny` som gör att TypeScript ger fel där `any` används eller härleds. Typen `any` är vanligtvis en källa till fel som kan dölja verkliga problem med dina typer. Undvik att använda den så mycket som möjligt. ================================================ FILE: website/src/content/docs/sv-se/book/readonly-properties.md ================================================ --- title: Skrivskyddade egenskaper sidebar: order: 13 label: 13. Skrivskyddade egenskaper --- Det är möjligt att förhindra skrivning till en egenskap genom att använda modifieraren `readonly` som ser till att egenskapen inte kan skrivas om, men den ger ingen garanti för total oföränderlighet: ```typescript interface Y { readonly a: number; } type X = { readonly a: number; }; type J = Readonly<{ a: number; }>; type K = { readonly [index: number]: string; }; ``` ================================================ FILE: website/src/content/docs/sv-se/book/strictnullchecks.md ================================================ --- title: strictNullChecks sidebar: order: 18 label: 18. strictNullChecks --- `strictNullChecks` är ett TypeScript-kompilatoralternativ som framtvingar strikt null-kontroll. När detta alternativ är aktiverat kan variabler och parametrar bara tilldelas `null` eller `undefined` om de uttryckligen har deklarerats att vara av den typen med hjälp av unionstypen `null` | `undefined`. Om en variabel eller parameter inte uttryckligen deklareras som nullable kommer TypeScript att generera ett fel för att förhindra potentiella körtidsfel. ================================================ FILE: website/src/content/docs/sv-se/book/symbols.md ================================================ --- title: Symboler sidebar: order: 58 label: 58. Symboler --- Symboler är en primitiv datatyp som representerar ett oföränderligt värde som garanterat är globalt unikt under hela programmets livstid. Symboler kan användas som nycklar för objektegenskaper och ger ett sätt att skapa icke-uppräkningsbara egenskaper. ```typescript const key1: symbol = Symbol('key1'); const key2: symbol = Symbol('key2'); const obj = { [key1]: 'value 1', [key2]: 'value 2', }; console.log(obj[key1]); // value 1 console.log(obj[key2]); // value 2 ``` I WeakMaps och WeakSets är symboler nu tillåtna som nycklar. ================================================ FILE: website/src/content/docs/sv-se/book/table-of-contents.md ================================================ --- title: Innehållsförteckning sidebar: order: 4 label: 4. Innehållsförteckning --- - Den koncisa TypeScript-boken - Översättningar - Nedladdningar och webbplats - Innehållsförteckning - Introduktion - Om författaren - Introduktion till TypeScript - Vad är TypeScript? - Varför TypeScript? - TypeScript och JavaScript - TypeScript-kodgenerering - Modernt JavaScript nu (Downleveling) - Komma igång med TypeScript - Installation - Konfiguration - TypeScript-konfigurationsfil - target - lib - strict - module - moduleResolution - esModuleInterop - jsx - skipLibCheck - files - include - exclude - importHelpers - Råd vid migrering till TypeScript - Utforska typsystemet - TypeScript-språktjänsten - Strukturell typning - Grundläggande jämförelseregler i TypeScript - Typer som mängder - Tilldela en typ: Typdeklarationer och typpåståenden - Typdeklaration - Typpåstående - Omgivande deklarationer - Egenskapskontroll och kontroll av överskottsegenskaper - Svaga typer - Strikt kontroll av objektliteraler (Freshness) - Typinferens - Mer avancerade inferenser - Typbreddning - Const - Const-modifierare på typparametrar - Const-påstående - Explicit typannotering - Typavsmalnande - Villkor - Kasta eller returnera - Diskriminerad union - Användardefinierade typvakter - Primitiva typer - string - boolean - number - bigInt - Symbol - null och undefined - Array - any - Typannoteringar - Valfria egenskaper - Skrivskyddade egenskaper - Indexsignaturer - Utöka typer - Literaltyper - Literalhärledning - strictNullChecks - Enums - Numeriska enums - Sträng-enums - Konstanta enums - Omvänd mappning - Omgivande enums - Beräknade och konstanta medlemmar - Avsmalning - typeof-typvakter - Sanningsvärdesavsmalning - Likhetsavsmalning - In-operatoravsmalning - instanceof-avsmalning - Tilldelningar - Kontrollflödesanalys - Typpredikat - Diskriminerade unioner - Never-typen - Uttömmande kontroll - Objekttyper - Tuppeltyp (Anonym) - Namngiven tuppeltyp (Märkt) - Tuppel med fast längd - Unionstyp - Intersektionstyper - Typindexering - Typ från värde - Typ från funktionsreturvärde - Typ från modul - Mappade typer - Modifierare för mappade typer - Villkorliga typer - Distributiva villkorliga typer - infer-typinferens i villkorliga typer - Fördefinierade villkorliga typer - Mall-unionstyper - Any-typen - Unknown-typen - Void-typen - Never-typen - Användning av Never-typen - Interface och Type - Gemensam syntax - Grundläggande typer - Objekt och Interface - Union- och Intersection-typer - Inbyggda primitiva typer - Vanliga inbyggda JS-objekt - Överlagringar - Sammanslagning och utökning - Skillnader mellan Type och Interface - Klass - Vanlig klasssyntax - Konstruktor - Privata och skyddade konstruktorer - Åtkomstmodifierare - Get och Set - Auto-accessorer i klasser - this - Parameteregenskaper - Abstrakta klasser - Med generics - Dekoratörer - Klassdekoratörer - Egenskapsdekoratör - Metoddekoratör - Getter- och setter-dekoratörer - Dekoratörmetadata - Arv - Statiska medlemmar - Egenskapsinitiering - Metodöverlagring - Generics - Generisk typ - Generiska klasser - Generiska begränsningar - Generisk kontextuell avsmalning - Raderade strukturella typer - Namnrymder - Symboler - Trippelsnedstreck-direktiv - Typmanipulation - Skapa typer från typer - Indexerade åtkomsttyper - Verktygstyper - Awaited\ - Partial\ - Required\ - Readonly\ - Record\ - Pick\ - Omit\ - Exclude\ - Extract\ - NonNullable\ - Parameters\ - ConstructorParameters\ - ReturnType\ - InstanceType\ - ThisParameterType\ - OmitThisParameter\ - ThisType\ - Uppercase\ - Lowercase\ - Capitalize\ - Uncapitalize\ - NoInfer\ - Övrigt - Fel och undantagshantering - Mixin-klasser - Asynkrona språkfunktioner - Iteratorer och generatorer - TsDocs JSDoc-referens - @types - JSX - ES6-moduler - ES7 exponentiationsoperator - for-await-of-satsen - Metaegenskapen new.target - Dynamiska importuttryck - "tsc –watch" - Non-null Assertion Operator - Standarddeklarationer - Valfri kedjning (Optional Chaining) - Nullish coalescing-operatorn - Mallsträngslitteraltyper (Template Literal Types) - Funktionsöverlagring - Rekursiva typer - Rekursiva villkorstyper - Stöd för ECMAScript-moduler i Node - Assertionsfunktioner - Variadiska tuppeltyper - Inkapslingstyper (Boxed types) - Kovarians och kontravarians i TypeScript - Valfria variansannotationer för typparametrar - Mallsträngsmönsterindexsignaturer - satisfies-operatorn - Importer och exporter av enbart typer - using-deklaration och explicit resurshantering - await using-deklaration - Importattribut ================================================ FILE: website/src/content/docs/sv-se/book/template-union-types.md ================================================ --- title: Mall-unionstyper sidebar: order: 43 label: 43. Mall-unionstyper --- Mall-unionstyper kan användas för att slå samman och manipulera text inuti typsystemet, till exempel: ```typescript type Status = 'active' | 'inactive'; type Products = 'p1' | 'p2'; type ProductId = `id-${Products}-${Status}`; // "id-p1-active" | "id-p1-inactive" | "id-p2-active" | "id-p2-inactive" ``` ================================================ FILE: website/src/content/docs/sv-se/book/the-concise-typescript-book.md ================================================ --- title: Den koncisa TypeScript-boken sidebar: order: 1 label: 1. Den koncisa TypeScript-boken --- Den koncisa TypeScript-boken ger en omfattande och kortfattad översikt över TypeScripts kapaciteter. Den erbjuder tydliga förklaringar som täcker alla aspekter i den senaste versionen av språket, från dess kraftfulla typsystem till avancerade funktioner. Oavsett om du är nybörjare eller en erfaren utvecklare är denna bok en ovärderlig resurs för att förbättra din förståelse och skicklighet i TypeScript. Denna bok är helt gratis och öppen källkod. Jag tror att teknisk utbildning av hög kvalitet bör vara tillgänglig för alla, vilket är anledningen till att jag håller denna bok gratis och öppen. Om boken hjälpte dig att lösa en bugg, förstå ett knepigt koncept eller avancera i din karriär, vänligen överväg att stödja mitt arbete genom att betala vad du vill (föreslaget pris: 15 USD) eller sponsra en kaffe. Ditt stöd hjälper mig att hålla innehållet uppdaterat och utöka det med nya exempel och djupare förklaringar. [![Buy Me a Coffee](https://img.shields.io/badge/buy_me_a_coffee-FFDD00?style=for-the-badge&logo=buy-me-a-coffee&logoColor=black)](https://www.buymeacoffee.com/simonepoggiali) [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/donate/?business=QW82ZS956XLFY&no_recurring=0¤cy_code=EUR) ================================================ FILE: website/src/content/docs/sv-se/book/the-never-type.md ================================================ --- title: Never-typen sidebar: order: 25 label: 25. Never-typen --- När en variabel avsmalnas till en typ som inte kan innehålla några värden, kommer TypeScript-kompilatorn att härleda att variabeln måste vara av typen `never`. Detta beror på att never-typen representerar ett värde som aldrig kan produceras. ```typescript const printValue = (val: string | number) => { if (typeof val === 'string') { console.log(val.toUpperCase()); } else if (typeof val === 'number') { console.log(val.toFixed(2)); } else { // val has type never here because it can never be anything other than a string or a number const neverVal: never = val; console.log(`Unexpected value: ${neverVal}`); } }; ``` ================================================ FILE: website/src/content/docs/sv-se/book/translations.md ================================================ --- title: Översättningar sidebar: order: 2 label: 2. Översättningar --- Denna bok har översatts till flera språkversioner, inklusive: [Kinesiska](https://github.com/gibbok/typescript-book/blob/main/README-zh_CN.md) [Italienska](https://github.com/gibbok/typescript-book/blob/main/README-it_IT.md) [Portugisiska (Brasilien)](https://github.com/gibbok/typescript-book/blob/main/README-pt_BR.md) [Svenska](https://github.com/gibbok/typescript-book/blob/main/README-sv_SE.md) ================================================ FILE: website/src/content/docs/sv-se/book/triple-slash-directives.md ================================================ --- title: Trippelsnedstreck-direktiv sidebar: order: 59 label: 59. Trippelsnedstreck-direktiv --- Trippelsnedstreck-direktiv är speciella kommentarer som ger instruktioner till kompilatorn om hur en fil ska bearbetas. Dessa direktiv börjar med tre på varandra följande snedstreck (`///`) och placeras vanligtvis överst i en TypeScript-fil och har ingen effekt på körbeteendet. Trippelsnedstreck-direktiv används för att referera till externa beroenden, specificera modulinläsningsbeteende, aktivera/inaktivera vissa kompilatorfunktioner, med mera. Några exempel: Referera till en deklarationsfil: ```typescript /// ``` Ange modulformat: ```typescript /// ``` Aktivera kompilatoralternativ, i följande exempel strikt läge: ```typescript /// ``` ================================================ FILE: website/src/content/docs/sv-se/book/tuple-type-anonymous.md ================================================ --- title: Tuppeltyp (Anonym) sidebar: order: 28 label: 28. Tuppeltyp (Anonym) --- En tuppeltyp är en typ som representerar en array med ett fast antal element och deras motsvarande typer. En tuppeltyp tvingar fram ett specifikt antal element och deras respektive typer i en fast ordning. Tuppeltyper är användbara när du vill representera en samling värden med specifika typer, där positionen för varje element i arrayen har en specifik betydelse. ```typescript type Point = [number, number]; ``` ================================================ FILE: website/src/content/docs/sv-se/book/type-annotations.md ================================================ --- title: Typannoteringar sidebar: order: 11 label: 11. Typannoteringar --- På variabler som deklareras med `var`, `let` och `const` är det möjligt att valfritt lägga till en typ: ```typescript const x: number = 1; ``` TypeScript är bra på att härleda typer, särskilt enkla sådana, så dessa deklarationer är i de flesta fall inte nödvändiga. På funktioner är det möjligt att lägga till typannoteringar på parametrar: ```typescript function sum(a: number, b: number) { return a + b; } ``` Följande är ett exempel med en anonym funktion (så kallad lambda-funktion): ```typescript const sum = (a: number, b: number) => a + b; ``` Dessa annoteringar kan undvikas när ett standardvärde för en parameter finns: ```typescript const sum = (a = 10, b: number) => a + b; ``` Returtypannoteringar kan läggas till på funktioner: ```typescript const sum = (a = 10, b: number): number => a + b; ``` Detta är särskilt användbart för mer komplexa funktioner eftersom det att explicit skriva returtypen före en implementation kan hjälpa till att bättre tänka igenom funktionen. Generellt sett, överväg att annotera typsignaturer men inte lokala variabler i funktionskroppen, och lägg alltid till typer på objektliteraler. ================================================ FILE: website/src/content/docs/sv-se/book/type-from-func-return.md ================================================ --- title: Typ från funktionsreturvärde sidebar: order: 35 label: 35. Typ från funktionsreturvärde --- Typ från funktionsreturvärde avser möjligheten att automatiskt härleda returtypen av en funktion baserat på dess implementation. Detta gör att TypeScript kan bestämma typen av det värde som returneras av funktionen utan explicita typannoteringar. ```typescript const add = (x: number, y: number) => x + y; // TypeScript can infer that the return type of the function is a number ``` ================================================ FILE: website/src/content/docs/sv-se/book/type-from-module.md ================================================ --- title: Typ från modul sidebar: order: 36 label: 36. Typ från modul --- Typ från modul avser möjligheten att använda en moduls exporterade värden för att automatiskt härleda deras typer. När en modul exporterar ett värde med en specifik typ kan TypeScript använda den informationen för att automatiskt härleda typen av det värdet när det importeras till en annan modul. ```typescript // calc.ts export const add = (x: number, y: number) => x + y; // index.ts import { add } from 'calc'; const r = add(1, 2); // r is number ``` ================================================ FILE: website/src/content/docs/sv-se/book/type-from-value.md ================================================ --- title: Typ från värde sidebar: order: 34 label: 34. Typ från värde --- Typ från värde i TypeScript avser den automatiska härledningen av en typ från ett värde eller uttryck genom typinferens. ```typescript const x = 'x'; // TypeScript infers 'x' as a string literal with 'const' (immutable), but widens it to 'string' with 'let' (reassignable). ``` ================================================ FILE: website/src/content/docs/sv-se/book/type-indexing.md ================================================ --- title: Typindexering sidebar: order: 33 label: 33. Typindexering --- Typindexering avser möjligheten att definiera typer som kan indexeras med en nyckel som inte är känd i förväg, genom att använda en indexsignatur för att specificera typen för egenskaper som inte uttryckligen deklarerats. ```typescript type Dictionary = { [key: string]: T; }; const myDict: Dictionary = { a: 'a', b: 'b' }; console.log(myDict['a']); // Returns a ``` ================================================ FILE: website/src/content/docs/sv-se/book/type-manipulation.md ================================================ --- title: Typmanipulation sidebar: order: 60 label: 60. Typmanipulation --- ### Skapa typer från typer Det är möjligt att skapa nya typer genom att komponera, manipulera eller transformera befintliga typer. Intersektionstyper (`&`): Gör det möjligt att kombinera flera typer till en enda typ: ```typescript type A = { foo: number }; type B = { bar: string }; type C = A & B; // Intersection of A and B const obj: C = { foo: 42, bar: 'hello' }; ``` Unionstyper (`|`): Gör det möjligt att definiera en typ som kan vara en av flera typer: ```typescript type Result = string | number; const value1: Result = 'hello'; const value2: Result = 42; ``` Mappade typer: Gör det möjligt att transformera egenskaperna hos en befintlig typ för att skapa en ny typ: ```typescript type Mutable = { readonly [P in keyof T]: T[P]; }; type Person = { name: string; age: number; }; type ImmutablePerson = Mutable; // Properties become read-only ``` Villkorliga typer: Gör det möjligt att skapa typer baserat på vissa villkor: ```typescript type ExtractParam = T extends (param: infer P) => any ? P : never; type MyFunction = (name: string) => number; type ParamType = ExtractParam; // string ``` ### Indexerade åtkomsttyper I TypeScript är det möjligt att komma åt och manipulera typerna av egenskaper inom en annan typ med hjälp av ett index, `Type[Key]`. ```typescript type Person = { name: string; age: number; }; type AgeType = Person['age']; // number ``` ```typescript type MyTuple = [string, number, boolean]; type MyType = MyTuple[2]; // boolean ``` ### Verktygstyper Flera inbyggda verktygstyper kan användas för att manipulera typer. Nedan följer en lista över de mest använda: #### Awaited\ Konstruerar en typ som rekursivt packar upp Promise-typer. ```typescript type A = Awaited>; // string ``` #### Partial\ Konstruerar en typ där alla egenskaper i T är satta som valfria. ```typescript type Person = { name: string; age: number; }; type A = Partial; // { name?: string | undefined; age?: number | undefined; } ``` #### Required\ Konstruerar en typ där alla egenskaper i T är satta som obligatoriska. ```typescript type Person = { name?: string; age?: number; }; type A = Required; // { name: string; age: number; } ``` #### Readonly\ Konstruerar en typ där alla egenskaper i T är satta som skrivskyddade. ```typescript type Person = { name: string; age: number; }; type A = Readonly; const a: A = { name: 'Simon', age: 17 }; a.name = 'John'; // Invalid ``` #### Record\ Konstruerar en typ med en uppsättning egenskaper K av typen T. ```typescript type Product = { name: string; price: number; }; const products: Record = { apple: { name: 'Apple', price: 0.5 }, banana: { name: 'Banana', price: 0.25 }, }; console.log(products.apple); // { name: 'Apple', price: 0.5 } ``` #### Pick\ Konstruerar en typ genom att välja ut de angivna egenskaperna K från T. ```typescript type Product = { name: string; price: number; }; type Price = Pick; // { price: number; } ``` #### Omit\ Konstruerar en typ genom att utelämna de angivna egenskaperna K från T. ```typescript type Product = { name: string; price: number; }; type Name = Omit; // { name: string; } ``` #### Exclude\ Konstruerar en typ genom att exkludera alla värden av typen U från T. ```typescript type Union = 'a' | 'b' | 'c'; type MyType = Exclude; // b ``` #### Extract\ Konstruerar en typ genom att extrahera alla värden av typen U från T. ```typescript type Union = 'a' | 'b' | 'c'; type MyType = Extract; // a | c ``` #### NonNullable\ Konstruerar en typ genom att exkludera null och undefined från T. ```typescript type Union = 'a' | null | undefined | 'b'; type MyType = NonNullable; // 'a' | 'b' ``` #### Parameters\ Extraherar parametertyperna för en funktionstyp T. ```typescript type Func = (a: string, b: number) => void; type MyType = Parameters; // [a: string, b: number] ``` #### ConstructorParameters\ Extraherar parametertyperna för en konstruktorfunktionstyp T. ```typescript class Person { constructor( public name: string, public age: number ) {} } type PersonConstructorParams = ConstructorParameters; // [name: string, age: number] const params: PersonConstructorParams = ['John', 30]; const person = new Person(...params); console.log(person); // Person { name: 'John', age: 30 } ``` #### ReturnType\ Extraherar returtypen för en funktionstyp T. ```typescript type Func = (name: string) => number; type MyType = ReturnType; // number ``` #### InstanceType\ Extraherar instanstypen för en klasstyp T. ```typescript class Person { name: string; constructor(name: string) { this.name = name; } sayHello() { console.log(`Hello, my name is ${this.name}!`); } } type PersonInstance = InstanceType; const person: PersonInstance = new Person('John'); person.sayHello(); // Hello, my name is John! ``` #### ThisParameterType\ Extraherar typen av 'this'-parametern från en funktionstyp T. ```typescript interface Person { name: string; greet(this: Person): void; } type PersonThisType = ThisParameterType; // Person ``` #### OmitThisParameter\ Tar bort 'this'-parametern från en funktionstyp T. ```typescript function capitalize(this: String) { return this[0].toUpperCase + this.substring(1).toLowerCase(); } type CapitalizeType = OmitThisParameter; // () => string ``` #### ThisType\ Fungerar som en markör för en kontextuell `this`-typ. ```typescript type Logger = { log: (error: string) => void; }; let helperFunctions: { [name: string]: Function } & ThisType = { hello: function () { this.log('some error'); // Valid as "log" is a part of "this". this.update(); // Invalid }, }; ``` #### Uppercase\ Gör namnet på indatatypen T till versaler. ```typescript type MyType = Uppercase<'abc'>; // "ABC" ``` #### Lowercase\ Gör namnet på indatatypen T till gemener. ```typescript type MyType = Lowercase<'ABC'>; // "abc" ``` #### Capitalize\ Gör första bokstaven i namnet på indatatypen T till versal. ```typescript type MyType = Capitalize<'abc'>; // "Abc" ``` #### Uncapitalize\ Gör första bokstaven i namnet på indatatypen T till gemen. ```typescript type MyType = Uncapitalize<'Abc'>; // "abc" ``` #### NoInfer\ NoInfer är en verktygstyp utformad för att blockera automatisk typinferens inom ramen för en generisk funktion. Exempel: ```typescript // Automatic inference of types within the scope of a generic function. function fn(x: T[], y: T) { return x.concat(y); } const r = fn(['a', 'b'], 'c'); // Type here is ("a" | "b" | "c")[] ``` Med NoInfer: ```typescript // Example function that uses NoInfer to prevent type inference function fn2(x: T[], y: NoInfer) { return x.concat(y); } const r2 = fn2(['a', 'b'], 'c'); // Error: Type Argument of type '"c"' is not assignable to parameter of type '"a" | "b"'. ``` ================================================ FILE: website/src/content/docs/sv-se/book/type-predicates.md ================================================ --- title: Typpredikat sidebar: order: 23 label: 23. Typpredikat --- Typpredikat i TypeScript är funktioner som returnerar ett booleskt värde och används för att avsmalma typen av en variabel till en mer specifik typ. ```typescript const isString = (value: unknown): value is string => typeof value === 'string'; const foo = (bar: unknown) => { if (isString(bar)) { console.log(bar.toUpperCase()); } else { console.log('not a string'); } }; ``` ================================================ FILE: website/src/content/docs/sv-se/book/typescript-introduction.md ================================================ --- title: Introduktion till TypeScript sidebar: order: 7 label: 7. Introduktion till TypeScript --- ### Vad är TypeScript? TypeScript är ett starkt typat programmeringsspråk som bygger på JavaScript. Det designades ursprungligen av Anders Hejlsberg 2012 och utvecklas och underhålls för närvarande av Microsoft som ett öppen källkod-projekt. TypeScript kompileras till JavaScript och kan köras i vilken JavaScript-runtime som helst (t.ex. en webbläsare eller Node.js på en server). Det stöder flera programmeringsparadigm såsom funktionell, generisk, imperativ och objektorienterad programmering, och är ett kompilerat (transpilerat) språk som konverteras till JavaScript före exekvering. ### Varför TypeScript? TypeScript är ett starkt typat språk som hjälper till att förhindra vanliga programmeringsmisstag och undvika vissa typer av körtidsfel innan programmet körs. Ett starkt typat språk gör det möjligt för utvecklaren att specificera olika programbegränsningar och beteenden i datatypsdefinitionerna, vilket underlättar möjligheten att verifiera programvarans korrekthet och förhindra defekter. Detta är särskilt värdefullt i storskaliga applikationer. Några av fördelarna med TypeScript: * Statisk typning, valfritt starkt typad * Typinferens * Tillgång till ES6- och ES7-funktioner * Plattforms- och webbläsarkompatibilitet * Verktygsstöd med IntelliSense ### TypeScript och JavaScript TypeScript skrivs i `.ts`- eller `.tsx`-filer, medan JavaScript-filer skrivs i `.js`- eller `.jsx`-filer. Filer med tillägget `.tsx` eller `.jsx` kan innehålla JavaScript Syntax Extension JSX, som används i React för UI-utveckling. TypeScript är en typad supermängd av JavaScript (ECMAScript 2015) vad gäller syntax. All JavaScript-kod är giltig TypeScript-kod, men det omvända är inte alltid sant. Betrakta till exempel en funktion i en JavaScript-fil med tillägget `.js`, som följande: ```typescript const sum = (a, b) => a + b; ``` Funktionen kan konverteras och användas i TypeScript genom att ändra filtillägget till `.ts`. Men om samma funktion annoteras med TypeScript-typer kan den inte köras i någon JavaScript-runtime utan kompilering. Följande TypeScript-kod kommer att producera ett syntaxfel om den inte kompileras: ```typescript const sum = (a: number, b: number): number => a + b; ``` TypeScript designades för att upptäcka möjliga undantag som kan uppstå vid körning under kompileringstiden genom att låta utvecklaren definiera avsikten med typannoteringar. Dessutom kan TypeScript också fånga problem om ingen typannotering tillhandahålls. Till exempel specificerar följande kodavsnitt inga TypeScript-typer: ```typescript const items = [{ x: 1 }, { x: 2 }]; const result = items.filter(item => item.y); ``` I detta fall upptäcker TypeScript ett fel och rapporterar: ```text Property 'y' does not exist on type '{ x: number; }'. ``` TypeScripts typsystem är till stor del influerat av körtidsbeteendet hos JavaScript. Till exempel kan additionsoperatorn (+), som i JavaScript kan utföra antingen strängkonkatenering eller numerisk addition, modelleras på samma sätt i TypeScript: ```typescript const result = '1' + 1; // Result is of type string ``` Teamet bakom TypeScript har fattat ett medvetet beslut att flagga ovanlig användning av JavaScript som fel. Betrakta till exempel följande giltiga JavaScript-kod: ```typescript const result = 1 + true; // In JavaScript, the result is equal 2 ``` Dock kastar TypeScript ett fel: ```text Operator '+' cannot be applied to types 'number' and 'boolean'. ``` Detta fel uppstår eftersom TypeScript strikt upprätthåller typkompatibilitet, och i detta fall identifierar det en ogiltig operation mellan en number och en boolean. ### TypeScript-kodgenerering TypeScript-kompilatorn har två huvudansvar: kontrollera typfel och kompilera till JavaScript. Dessa två processer är oberoende av varandra. Typer påverkar inte kodens exekvering i en JavaScript-runtime, eftersom de raderas helt under kompileringen. TypeScript kan fortfarande producera JavaScript även vid typfel. Här är ett exempel på TypeScript-kod med ett typfel: ```typescript const add = (a: number, b: number): number => a + b; const result = add('x', 'y'); // Argument of type 'string' is not assignable to parameter of type 'number'. ``` Trots detta kan den fortfarande producera körbar JavaScript-utdata: ```typescript 'use strict'; const add = (a, b) => a + b; const result = add('x', 'y'); // xy ``` Det är inte möjligt att kontrollera TypeScript-typer vid körning. Till exempel: ```typescript interface Animal { name: string; } interface Dog extends Animal { bark: () => void; } interface Cat extends Animal { meow: () => void; } const makeNoise = (animal: Animal) => { if (animal instanceof Dog) { // 'Dog' only refers to a type, but is being used as a value here. // ... } }; ``` Eftersom typerna raderas efter kompilering finns det inget sätt att köra denna kod i JavaScript. För att känna igen typer vid körning behöver vi använda en annan mekanism. TypeScript erbjuder flera alternativ, där ett vanligt är "tagged union". Till exempel: ```typescript interface Dog { kind: 'dog'; // Tagged union bark: () => void; } interface Cat { kind: 'cat'; // Tagged union meow: () => void; } type Animal = Dog | Cat; const makeNoise = (animal: Animal) => { if (animal.kind === 'dog') { animal.bark(); } else { animal.meow(); } }; const dog: Dog = { kind: 'dog', bark: () => console.log('bark'), }; makeNoise(dog); ``` Egenskapen "kind" är ett värde som kan användas vid körning för att särskilja mellan objekt i JavaScript. Det är också möjligt att ett värde vid körning har en annan typ än den som deklarerades i typdeklarationen. Till exempel om utvecklaren har misstolkat en API-typ och annoterat den felaktigt. TypeScript är en supermängd av JavaScript, så nyckelordet "class" kan användas som en typ och ett värde vid körning. ```typescript class Animal { constructor(public name: string) {} } class Dog extends Animal { constructor( public name: string, public bark: () => void ) { super(name); } } class Cat extends Animal { constructor( public name: string, public meow: () => void ) { super(name); } } type Mammal = Dog | Cat; const makeNoise = (mammal: Mammal) => { if (mammal instanceof Dog) { mammal.bark(); } else { mammal.meow(); } }; const dog = new Dog('Fido', () => console.log('bark')); makeNoise(dog); ``` I JavaScript har en "class" en "prototype"-egenskap, och "instanceof"-operatorn kan användas för att testa om prototype-egenskapen för en konstruktor förekommer någonstans i prototypkedjan för ett objekt. TypeScript har ingen effekt på körtidsprestanda, eftersom alla typer raderas. Dock introducerar TypeScript viss overhead vid byggtid. ### Modernt JavaScript nu (Downleveling) TypeScript kan kompilera kod till vilken utgiven version av JavaScript som helst sedan ECMAScript 3 (1999). Detta innebär att TypeScript kan transpilera kod från de senaste JavaScript-funktionerna till äldre versioner, en process som kallas Downleveling. Detta möjliggör användning av modernt JavaScript samtidigt som maximal kompatibilitet med äldre körtidsmiljöer bibehålls. Det är viktigt att notera att vid transpilering till en äldre version av JavaScript kan TypeScript generera kod som kan medföra en prestandaoverhead jämfört med nativa implementeringar. Här är några av de moderna JavaScript-funktioner som kan användas i TypeScript: * ECMAScript-moduler istället för AMD-style "define"-callbacks eller CommonJS "require"-satser. * Klasser istället för prototyper. * Variabeldeklaration med "let" eller "const" istället för "var". * "for-of"-loop eller ".forEach" istället för den traditionella "for"-loopen. * Pilfunktioner istället för funktionsuttryck. * Destruktureringstilldelning. * Förkortade egenskaps-/metodnamn och beräknade egenskapsnamn. * Standardparametrar för funktioner. Genom att utnyttja dessa moderna JavaScript-funktioner kan utvecklare skriva mer uttrycksfull och koncis kod i TypeScript. ================================================ FILE: website/src/content/docs/sv-se/book/union-type.md ================================================ --- title: Unionstyp sidebar: order: 31 label: 31. Unionstyp --- En unionstyp är en typ som representerar ett värde som kan vara en av flera typer. Unionstyper betecknas med symbolen `|` mellan varje möjlig typ. ```typescript let x: string | number; x = 'hello'; // Valid x = 123; // Valid ``` ================================================ FILE: website/src/content/docs/sv-se/book/unknown-type.md ================================================ --- title: Unknown-typen sidebar: order: 45 label: 45. Unknown-typen --- I TypeScript representerar `unknown`-typen ett värde av en okänd typ. Till skillnad från `any`-typen, som tillåter vilken typ av värde som helst, kräver `unknown` en typkontroll eller assertion innan den kan användas på ett specifikt sätt, så inga operationer är tillåtna på en `unknown` utan att först göra en assertion eller avsmalning till en mer specifik typ. `unknown`-typen kan bara tilldelas till any-typen och `unknown`-typen själv, den är ett typsäkert alternativ till `any`. ```typescript let value: unknown; let value1: unknown = value; // Valid let value2: any = value; // Valid let value3: boolean = value; // Invalid let value4: number = value; // Invalid ``` ```typescript const add = (a: unknown, b: unknown): number | undefined => typeof a === 'number' && typeof b === 'number' ? a + b : undefined; console.log(add(1, 2)); // 3 console.log(add('x', 2)); // undefined ``` ================================================ FILE: website/src/content/docs/sv-se/book/void-type.md ================================================ --- title: Void-typen sidebar: order: 46 label: 46. Void-typen --- Typen `void` används för att indikera att en funktion inte returnerar något värde. ```typescript const sayHello = (): void => { console.log('Hello!'); }; ``` ================================================ FILE: website/src/content/docs/sv-se/index.mdx ================================================ --- title: TypeScript Book description: The Concise TypeScript Book template: splash hero: tagline: Den koncisa TypeScript-boken ger en omfattande och kortfattad översikt över TypeScripts kapaciteter. Den erbjuder tydliga förklaringar som täcker alla aspekter i den senaste versionen av språket, från dess kraftfulla typsystem till avancerade funktioner. Oavsett om du är nybörjare eller en erfaren utvecklare är denna bok en ovärderlig resurs för att förbättra din förståelse och skicklighet i TypeScript.

Denna bok är helt gratis och öppen källkod. actions: - text: Läs nu! link: /typescript-book/sv-se/book/the-concise-typescript-book/ icon: right-arrow variant: primary - text: GitHub link: https://github.com/gibbok/typescript-book icon: github variant: secondary - text: X.com link: https://twitter.com/gibbok_coding icon: x.com variant: secondary --- ================================================ FILE: website/src/content/docs/zh-cn/book/about-the-author.md ================================================ --- title: 关于作者 sidebar: order: 6 label: 6. 关于作者 --- Simone Poggiali 是一位经验丰富的资深工程师,自 90 年代以来便对编写专业级代码充满热情。在他的国际职业生涯中,他曾为众多客户(从初创公司到大型企业)参与过各种项目。HelloFresh、西门子、O2、Leroy Merlin 和 Snowplow 等知名公司都曾受益于他的专业知识和敬业精神。 您可以通过以下平台联系 Simone Poggiali: * 领英: [https://www.linkedin.com/in/simone-poggiali](https://www.linkedin.com/in/simone-poggiali) * GitHub: [https://github.com/gibbok](https://github.com/gibbok) * X.com: [https://x.com/gibbok_coding](https://x.com/gibbok_coding) * 电子邮箱: gibbok.coding📧gmail.com 完整的贡献者名单:[https://github.com/gibbok/typescript-book/graphs/contributors](https://github.com/gibbok/typescript-book/graphs/contributors) ================================================ FILE: website/src/content/docs/zh-cn/book/any-type.md ================================================ --- title: 任意类型 sidebar: order: 44 label: 44. 任意类型 --- `any` 类型是一种特殊类型(通用超类型),可用于表示任何类型的值(基元、对象、数组、函数、错误、符号)。它通常用于编译时未知值类型的情况,或者使用来自没有 TypeScript 类型的外部 API 或库的值时。 使用 `any` 类型,就等于告诉 TypeScript 编译器,值应该不受任何限制地表示。为了最大限度地提高代码的类型安全性,请考虑以下几点: * 将 `any` 的使用限制在类型确实未知的特定情况下。 * 不要从函数返回 `any` 类型,因为这会削弱使用该函数的代码的类型安全性。 * 如果您需要使编译器保持沉默,请使用 `@ts-ignore` 而不是 `any`。 ```typescript let value: any; value = true; // 有效 value = 7; // 有效 ``` ================================================ FILE: website/src/content/docs/zh-cn/book/assignments.md ================================================ --- title: 赋值 sidebar: order: 21 label: 21. 赋值 --- 使用赋值缩小 TypeScript 是一种根据分配给变量的值来缩小变量类型的方法。当为变量分配值时,TypeScript 会根据分配的值推断其类型,并缩小变量的类型以匹配推断的类型。 ```typescript let value: string | number; value = 'hello'; if (typeof value === 'string') { console.log(value.toUpperCase()); } value = 42; if (typeof value === 'number') { console.log(value.toFixed(2)); } ``` ================================================ FILE: website/src/content/docs/zh-cn/book/built-in-type-primitives.md ================================================ --- title: 内置原始数据类型 sidebar: order: 49 label: 49. 内置原始数据类型 --- TypeScript 有几个内置的原属数据类型,可用于定义变量、函数参数和返回类型: `number`: 表示数值,包括整数和浮点数。 `string`: 代表文本数据。 `boolean`: 代表逻辑值,可以是 true 或 false。 `null`: 表示没有值。 `undefined`: 表示尚未赋值或未定义的值。 `symbol`: 代表唯一标识符。符号通常用作对象属性的键。 `bigint`: 表示任意精度整数。 `any`: 代表动态或未知类型。any 类型的变量可以保存任何类型的值,并且它们绕过类型检查。 `void`: 表示不存在任何类型。它通常用作不返回值的函数的返回类型。 `never`: 表示从未出现过的值的类型。它通常用作引发错误或进入无限循环的函数的返回类型。 ================================================ FILE: website/src/content/docs/zh-cn/book/class.md ================================================ --- title: Class sidebar: order: 54 label: 54. Class --- ### 通用语法 TypeScript 中使用关键字 `class` 来定义类。下面,您可以看到一个示例: ```typescript class Person { private name: string; private age: number; constructor(name: string, age: number) { this.name = name; this.age = age; } public sayHi(): void { console.log( `Hello, my name is ${this.name} and I am ${this.age} years old.` ); } } ``` `class` 关键字用于定义名为 `Person` 的类。 该类有两个私有属性:类型名称 `string` 和类型年龄 `number`。 构造函数是使用 `constructor` 关键字定义的。它将姓名和年龄作为参数并将它们分配给相应的属性。 该类有一个 `public` 名为 `sayHi` 的方法,用于记录问候消息。 要在 TypeScript 中创建类的实例,可以使用 `new` 关键字,后跟类名,然后使用括号 `()`。例如: ```typescript const myObject = new Person('John Doe', 25); myObject.sayHi(); // 输出:Hello, my name is John Doe and I am 25 years old. ``` ### 构造函数 构造函数是类中的特殊方法,用于在创建类的实例时初始化对象的属性。 ```typescript class Person { public name: string; public age: number; constructor(name: string, age: number) { this.name = name; this.age = age; } sayHello() { console.log( `Hello, my name is ${this.name} and I'm ${this.age} years old.` ); } } const john = new Person('Simon', 17); john.sayHello(); ``` 可以使用以下语法重载构造函数: ```typescript type Sex = 'm' | 'f'; class Person { name: string; age: number; sex: Sex; constructor(name: string, age: number, sex?: Sex); constructor(name: string, age: number, sex: Sex) { this.name = name; this.age = age; this.sex = sex ?? 'm'; } } const p1 = new Person('Simon', 17); const p2 = new Person('Alice', 22, 'f'); ``` 在 TypeScript 中,可以定义多个构造函数重载,但只能有一个必须与所有重载兼容的实现,这可以通过使用可选参数来实现。 ```typescript class Person { name: string; age: number; constructor(); constructor(name: string); constructor(name: string, age: number); constructor(name?: string, age?: number) { this.name = name ?? 'Unknown'; this.age = age ?? 0; } displayInfo() { console.log(`Name: ${this.name}, Age: ${this.age}`); } } const person1 = new Person(); person1.displayInfo(); // Name: Unknown, Age: 0 const person2 = new Person('John'); person2.displayInfo(); // Name: John, Age: 0 const person3 = new Person('Jane', 25); person3.displayInfo(); // Name: Jane, Age: 25 ``` ### 私有和受保护的构造函数 在 TypeScript 中,构造函数可以标记为私有或受保护,这限制了它们的可访问性和使用。 私有构造函数:只能在类本身内调用。私有构造函数通常用于以下场景:您想要强制执行单例模式或将实例的创建限制为类中的工厂方法 受保护的构造函数:当您想要创建一个不应直接实例化但可以由子类扩展的基类时,受保护的构造函数非常有用。 ```typescript class BaseClass { protected constructor() {} } class DerivedClass extends BaseClass { private value: number; constructor(value: number) { super(); this.value = value; } } // 尝试直接实例化基类将导致错误 // const baseObj = new BaseClass(); // 错误:类"BaseClass"的构造函数受到保护。 // 创建派生类的实例 const derivedObj = new DerivedClass(10); ``` ### 访问修饰符 访问修饰符 `private` 、`protected` 和 `public` 用于控制 TypeScript 类中类成员(例如属性和方法)的可见性和可访问性。这些修饰符对于强制封装以及建立访问和修改类内部状态的边界至关重要。 修饰符 `private` 仅限制对包含类中的类成员的访问。 修饰符 `protected` 允许访问包含类及其派生类中的类成员。 修饰符 `public` 提供对类成员的不受限制的访问,允许从任何地方访问它。 ### Get 与 Set Getter 和 Setter 是特殊方法,允许您定义类属性的自定义访问和修改行为。它们使您能够封装对象的内部状态,并在获取或设置属性值时提供附加逻辑。在 TypeScript 中,getter 和 setter 分别使用 `get` 和 `set` 关键字定义。这是一个例子: ```typescript class MyClass { private _myProperty: string; constructor(value: string) { this._myProperty = value; } get myProperty(): string { return this._myProperty; } set myProperty(value: string) { this._myProperty = value; } } ``` ### 类中的自动访问器 TypeScript 版本 4.9 添加了对自动访问器的支持,这是即将推出的 ECMAScript 功能。它们类似于类属性,但使用"accessor"关键字声明。 ```typescript class Animal { accessor name: string; constructor(name: string) { this.name = name; } } ``` 自动访问器被"脱糖"为私有get访问set器,在无法访问的属性上运行。 ```typescript class Animal { #__name: string; get name() { return this.#__name; } set name(value: string) { this.#__name = name; } constructor(name: string) { this.name = name; } } ``` ### this 在 TypeScript 中,`this` 关键字指的是类的方法或构造函数中的当前实例。它允许您在类自己的范围内访问和修改类的属性和方法。它提供了一种在对象自己的方法中访问和操作对象内部状态的方法。 ```typescript class Person { private name: string; constructor(name: string) { this.name = name; } public introduce(): void { console.log(`Hello, my name is ${this.name}.`); } } const person1 = new Person('Alice'); person1.introduce(); // Hello, my name is Alice. ``` ### 参数属性 参数属性允许您直接在构造函数参数中声明和初始化类属性,从而避免样板代码,例如: ```typescript class Person { constructor( private name: string, public age: number ) { // 构造函数中的"private"和"public"关键字自动声明并初始化相应的类属性。 } public introduce(): void { console.log( `Hello, my name is ${this.name} and I am ${this.age} years old.` ); } } const person = new Person('Alice', 25); person.introduce(); ``` ### 抽象类 抽象类在 TypeScript 中主要用于继承,它们提供了一种定义可由子类继承的公共属性和方法的方法。当您想要定义常见行为并强制子类实现某些方法时,这非常有用。它们提供了一种创建类层次结构的方法,其中抽象基类为子类提供共享接口和通用功能。 ```typescript abstract class Animal { protected name: string; constructor(name: string) { this.name = name; } abstract makeSound(): void; } class Cat extends Animal { makeSound(): void { console.log(`${this.name} meows.`); } } const cat = new Cat('Whiskers'); cat.makeSound(); // 输出:Whiskers meows. ``` ### 使用泛型 具有泛型的类允许您定义可以与不同类型一起使用的可重用类。 ```typescript class Container { private item: T; constructor(item: T) { this.item = item; } getItem(): T { return this.item; } setItem(item: T): void { this.item = item; } } const container1 = new Container(42); console.log(container1.getItem()); // 42 const container2 = new Container('Hello'); container2.setItem('World'); console.log(container2.getItem()); // World ``` ### 装饰器 装饰器提供了一种添加元数据、修改行为、验证或扩展目标元素功能的机制。它们是在运行时执行的函数。多个装饰器可以应用于一个声明。 装饰器是实验性功能,以下示例仅与使用 ES6 的 TypeScript 版本 5 或更高版本兼容。 对于 5 之前的 TypeScript 版本,应在您的 `tsconfig.json` 中使用使`experimentalDecorators` 或在命令行中使用 `--experimentalDecorators` 来启用它们(但以下示例不起作用)。 装饰器的一些常见用例包括: * 观察属性变化。 * 观察方法调用。 * 添加额外的属性或方法。 * 运行时验证。 * 自动序列化和反序列化。 * 日志记录。 * 授权和认证。 * 错误防护。 注意:版本 5 的装饰器不允许装饰参数。 装饰器的类型: #### 类装饰器 类装饰器对于扩展现有类非常有用,例如添加属性或方法,或者收集类的实例。在下面的示例中,我们添加一个 `toString` 将类转换为字符串表示形式的方法。 ```typescript type Constructor = new (...args: any[]) => T; function toString( Value: Class, context: ClassDecoratorContext ) { return class extends Value { constructor(...args: any[]) { super(...args); console.log(JSON.stringify(this)); console.log(JSON.stringify(context)); } }; } @toString class Person { name: string; constructor(name: string) { this.name = name; } greet() { return 'Hello, ' + this.name; } } const person = new Person('Simon'); /* Logs: {"name":"Simon"} {"kind":"class","name":"Person"} */ ``` #### 属性装饰器 属性装饰器对于修改属性的行为非常有用,例如更改初始化值。在下面的代码中,我们有一个脚本将属性设置为始终大写: ```typescript function upperCase( target: undefined, context: ClassFieldDecoratorContext ) { return function (this: T, value: string) { return value.toUpperCase(); }; } class MyClass { @upperCase prop1 = 'hello!'; } console.log(new MyClass().prop1); // 日志:HELLO! ``` #### 方法装饰器 方法装饰器允许您更改或增强方法的行为。下面是一个简单记录器的示例: ```typescript function log( target: (this: This, ...args: Args) => Return, context: ClassMethodDecoratorContext< This, (this: This, ...args: Args) => Return > ) { const methodName = String(context.name); function replacementMethod(this: This, ...args: Args): Return { console.log(`LOG: Entering method '${methodName}'.`); const result = target.call(this, ...args); console.log(`LOG: Exiting method '${methodName}'.`); return result; } return replacementMethod; } class MyClass { @log sayHello() { console.log('Hello!'); } } new MyClass().sayHello(); ``` 它记录: ```shell LOG: Entering method 'sayHello'. Hello! LOG: Exiting method 'sayHello'. ``` #### Getter 和 Setter 装饰器 getter 和 setter 装饰器允许您更改或增强类访问器的行为。例如,它们对于验证属性分配很有用。这是 getter 装饰器的一个简单示例: ```typescript function range(min: number, max: number) { return function ( target: (this: This) => Return, context: ClassGetterDecoratorContext ) { return function (this: This): Return { const value = target.call(this); if (value < min || value > max) { throw 'Invalid'; } Object.defineProperty(this, context.name, { value, enumerable: true, }); return value; }; }; } class MyClass { private _value = 0; constructor(value: number) { this._value = value; } @range(1, 100) get getValue(): number { return this._value; } } const obj = new MyClass(10); console.log(obj.getValue); // 有效: 10 const obj2 = new MyClass(999); console.log(obj2.getValue); // 抛出异常: Invalid! ``` ### 装饰器元数据 装饰器元数据简化了装饰器在任何类中应用和利用元数据的过程。 他们可以访问上下文对象上的新元数据属性,该属性可以充当基元和对象的密钥。 可以通过"Symbol.metadata"在类上访问元数据信息。 元数据可用于各种目的,例如调试、序列化或使用装饰器的依赖项注入。 ```typescript //@ts-ignore Symbol.metadata ??= Symbol('Symbol.metadata'); // 简单的兼容性填充 type Context = | ClassFieldDecoratorContext | ClassAccessorDecoratorContext | ClassMethodDecoratorContext; // 上下文对象包含属性元数据: 装饰器元数据 function setMetadata(_target: any, context: Context) { // 使用基本类型值设置元数据对象 context.metadata[context.name] = true; } class MyClass { @setMetadata a = 123; @setMetadata accessor b = 'b'; @setMetadata fn() {} } const metadata = MyClass[Symbol.metadata]; // 获取元数据对象信息 console.log(JSON.stringify(metadata)); // {"bar":true,"baz":true,"foo":true} ``` ### 继承 继承是指一个类可以从另一个类(称为基类或超类)继承属性和方法的机制。派生类也称为子类或子类,可以通过添加新的属性和方法或重写现有的属性和方法来扩展和专门化基类的功能。 ```typescript class Animal { name: string; constructor(name: string) { this.name = name; } speak(): void { console.log('The animal makes a sound'); } } class Dog extends Animal { breed: string; constructor(name: string, breed: string) { super(name); this.breed = breed; } speak(): void { console.log('Woof! Woof!'); } } // 创建基类的一个实例 const animal = new Animal('Generic Animal'); animal.speak(); // The animal makes a sound // 创建派生类的一个实例 const dog = new Dog('Max', 'Labrador'); dog.speak(); // Woof! Woof!" ``` TypeScript 不支持传统意义上的多重继承,而是允许从单个基类继承。TypeScript 支持多种接口。接口可以定义对象结构的契约,类可以实现多个接口。这允许类从多个源继承行为和结构。 ```typescript interface Flyable { fly(): void; } interface Swimmable { swim(): void; } class FlyingFish implements Flyable, Swimmable { fly() { console.log('Flying...'); } swim() { console.log('Swimming...'); } } const flyingFish = new FlyingFish(); flyingFish.fly(); flyingFish.swim(); ``` TypeScript 中的关键字 `class` 与 JavaScript 类似,通常被称为语法糖。它是在 ECMAScript 2015 (ES6) 中引入的,旨在提供更熟悉的语法,以基于类的方式创建和使用对象。然而,值得注意的是,TypeScript 作为 JavaScript 的超集,最终会编译为 JavaScript,而 JavaScript 的核心仍然是基于原型的。 ### 静态成员 TypeScript 有静态成员。要访问类的静态成员,可以使用类名后跟一个点,而不需要创建对象。 ```typescript class OfficeWorker { static memberCount: number = 0; constructor(private name: string) { OfficeWorker.memberCount++; } } const w1 = new OfficeWorker('James'); const w2 = new OfficeWorker('Simon'); const total = OfficeWorker.memberCount; console.log(total); // 2 ``` ### 属性初始化 在 TypeScript 中初始化类的属性有多种方法: 内嵌: 在下面的示例中,创建类的实例时将使用这些初始值。 ```typescript class MyClass { property1: string = 'default value'; property2: number = 42; } ``` 在构造函数中: ```typescript class MyClass { property1: string; property2: number; constructor() { this.property1 = 'default value'; this.property2 = 42; } } ``` 使用构造函数参数: ```typescript class MyClass { constructor( private property1: string = 'default value', public property2: number = 42 ) { // 无需显式地将值分配给属性。 } log() { console.log(this.property2); } } const x = new MyClass(); x.log(); ``` ### 方法重载 方法重载允许一个类有多个名称相同但参数类型不同或参数数量不同的方法。这允许我们根据传递的参数以不同的方式调用方法。 ```typescript class MyClass { add(a: number, b: number): number; // 重载签名 1 add(a: string, b: string): string; // 重载签名 2 add(a: number | string, b: number | string): number | string { if (typeof a === 'number' && typeof b === 'number') { return a + b; } if (typeof a === 'string' && typeof b === 'string') { return a.concat(b); } throw new Error('Invalid arguments'); } } const r = new MyClass(); console.log(r.add(10, 5)); // 日志:15 ``` ================================================ FILE: website/src/content/docs/zh-cn/book/common-built-in-js-objects.md ================================================ --- title: 常见的内置JS对象 sidebar: order: 50 label: 50. 常见的内置JS对象 --- TypeScript 是 JavaScript 的超集,它包含所有常用的内置 JavaScript 对象。您可以在 Mozilla 开发者网络 (MDN) 文档网站上找到这些对象的详细列表: [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects) 以下是一些常用的内置 JavaScript 对象的列表: * Function * Object * Boolean * Error * Number * BigInt * Math * Date * String * RegExp * Array * Map * Set * Promise * Intl ================================================ FILE: website/src/content/docs/zh-cn/book/conditional-types.md ================================================ --- title: 条件类型 sidebar: order: 39 label: 39. 条件类型 --- 条件类型是一种创建依赖于条件的类型的方法,其中要创建的类型是根据条件的结果确定的。它们是使用 `extends` 关键字和三元运算符来定义的,以便有条件地在两种类型之间进行选择。 ```typescript type IsArray = T extends any[] ? true : false; const myArray = [1, 2, 3]; const myNumber = 42; type IsMyArrayAnArray = IsArray; // true 类型 type IsMyNumberAnArray = IsArray; // false 类型 ``` ================================================ FILE: website/src/content/docs/zh-cn/book/control-flow-analysis.md ================================================ --- title: 控制流分析 sidebar: order: 22 label: 22. 控制流分析 --- TypeScript 中的控制流分析是一种静态分析代码流以推断变量类型的方法,允许编译器根据分析结果根据需要缩小这些变量的类型。 在 TypeScript 4.4 之前,代码流分析仅适用于 if 语句中的代码,但从 TypeScript 4.4 开始,它还可以应用于条件表达式和通过 const 变量间接引用的判别式属性访问。 例如: ```typescript const f1 = (x: unknown) => { const isString = typeof x === 'string'; if (isString) { x.length; } }; const f2 = ( obj: { kind: 'foo'; foo: string } | { kind: 'bar'; bar: number } ) => { const isFoo = obj.kind === 'foo'; if (isFoo) { obj.foo; } else { obj.bar; } }; ``` 一些未发生缩小的示例: ```typescript const f1 = (x: unknown) => { let isString = typeof x === 'string'; if (isString) { x.length; // 错误, 没有缩小,因为 isString 不是常量 } }; const f6 = ( obj: { kind: 'foo'; foo: string } | { kind: 'bar'; bar: number } ) => { const isFoo = obj.kind === 'foo'; obj = obj; if (isFoo) { obj.foo; // 错误, 没有缩小,因为 obj 在函数体中被赋值 } }; ``` 注意: 在条件表达式中最多分析五个间接级别。 ================================================ FILE: website/src/content/docs/zh-cn/book/differences-between-type-and-interface.md ================================================ --- title: 类型和接口之间的差异 sidebar: order: 53 label: 53. 类型和接口之间的差异 --- 声明合并(增强): 接口支持声明合并,这意味着您可以定义多个具有相同名称的接口,TypeScript 会将它们合并为具有组合属性和方法的单个接口。 另一方面,类型不支持声明合并。 当您想要添加额外的功能或自定义现有类型而不修改原始定义或修补丢失或不正确的类型时,这可能会很有帮助。 ```typescript interface A { x: string; } interface A { y: string; } const j: A = { x: 'xx', y: 'yy', }; ``` 扩展其他类型/接口: 类型和接口都可以扩展其他类型/接口,但语法不同。 对于接口,您可以使用“extends”关键字从其他接口继承属性和方法。 但是,接口无法扩展像联合类型这样的复杂类型。 ```typescript interface A { x: string; y: number; } interface B extends A { z: string; } const car: B = { x: 'x', y: 123, z: 'z', }; ``` 对于类型,您可以使用 & 运算符将多个类型合并为单个类型(交集)。 ```typescript interface A { x: string; y: number; } type B = A & { j: string; }; const c: B = { x: 'x', y: 123, j: 'j', }; ``` 并集和交集类型: 在定义并集和交集类型时,类型更加灵活。 通过“type”关键字,您可以使用“|”运算符轻松创建联合类型,并使用“&”运算符创建交集类型。 虽然接口也可以间接表示联合类型,但它们没有对交集类型的内置支持。 ```typescript type Department = 'dep-x' | 'dep-y'; // 并集 type Person = { name: string; age: number; }; type Employee = { id: number; department: Department; }; type EmployeeInfo = Person & Employee; // 交集 ``` 接口示例: ```typescript interface A { x: 'x'; } interface B { y: 'y'; } type C = A | B; // 接口的并集 ``` ================================================ FILE: website/src/content/docs/zh-cn/book/discriminated-unions.md ================================================ --- title: 可区分联合 sidebar: order: 24 label: 24. 可区分联合 --- TypeScript 中的可区分联合是一种联合类型,它使用称为判别式的公共属性来缩小联合的可能类型集。 ```typescript type Square = { kind: 'square'; // 判别式 size: number; }; type Circle = { kind: 'circle'; // 判别式 radius: number; }; type Shape = Square | Circle; const area = (shape: Shape) => { switch (shape.kind) { case 'square': return Math.pow(shape.size, 2); case 'circle': return Math.PI * Math.pow(shape.radius, 2); } }; const square: Square = { kind: 'square', size: 5 }; const circle: Circle = { kind: 'circle', radius: 2 }; console.log(area(square)); // 25 console.log(area(circle)); // 12.566370614359172 ``` ================================================ FILE: website/src/content/docs/zh-cn/book/distributive-conditional-types.md ================================================ --- title: 分配条件类型 sidebar: order: 40 label: 40. 分配条件类型 --- 分布式条件类型是一种功能,通过单独对联合的每个成员应用转换,允许类型分布在类型的联合上。当使用映射类型或高阶类型时,这尤其有用。 ```typescript type Nullable = T extends any ? T | null : never; type NumberOrBool = number | boolean; type NullableNumberOrBool = Nullable; // number | boolean | null ``` ================================================ FILE: website/src/content/docs/zh-cn/book/downloads-and-website.md ================================================ --- title: 下载和网站 sidebar: order: 3 label: 3. 下载和网站 --- 您还可以下载 Epub 版本: [https://github.com/gibbok/typescript-book/tree/main/downloads](https://github.com/gibbok/typescript-book/tree/main/downloads) 在线版本可在以下位置获得: [https://gibbok.github.io/typescript-book](https://gibbok.github.io/typescript-book) ================================================ FILE: website/src/content/docs/zh-cn/book/enums.md ================================================ --- title: 枚举 sidebar: order: 19 label: 19. 枚举 --- 在 TypeScript 中,枚举是一组命名常量值。 ```typescript enum Color { Red = '#ff0000', Green = '#00ff00', Blue = '#0000ff', } ``` 枚举可以用不同的方式定义: ### 数字枚举 在 TypeScript 中,数字枚举是一个枚举,其中每个常量都分配有一个数值,默认从 0 开始。 ```typescript enum Size { Small, // 值从 0 开始 Medium, Large, } ``` 可以通过显式分配来指定自定义值: ```typescript enum Size { Small = 10, Medium, Large, } console.log(Size.Medium); // 11 ``` ### 字符串枚举 在 TypeScript 中,字符串枚举是每个常量都分配有一个字符串值的枚举。 ```typescript enum Language { English = 'EN', Spanish = 'ES', } ``` 注意:TypeScript 允许使用异构枚举,其中字符串和数字成员可以共存。 ### 常量枚举 TypeScript 中的常量枚举是一种特殊类型的枚举,其中所有值在编译时都是已知的,并且在使用枚举的任何地方都会内联,从而产生更高效的代码。 ```typescript const enum Language { English = 'EN', Spanish = 'ES', } console.log(Language.English); ``` 将被编译成: ```typescript console.log('EN' /* Language.English */); ``` 注意:常量枚举具有硬编码值,擦除枚举,这在独立库中可能更有效,但通常是不可取的。此外,常量枚举不能有计算成员。 ### 反向映射 在 TypeScript 中,枚举中的反向映射是指从值中检索枚举成员名称的能力。默认情况下,枚举成员具有从名称到值的正向映射,但可以通过为每个成员显式设置值来创建反向映射。当您需要按枚举成员的值查找枚举成员,或者需要迭代所有枚举成员时,反向映射非常有用。需要注意的是,只有数字类型的枚举成员会生成反向映射,字符串类型的枚举成员则不会。 以下枚举: ```typescript enum Grade { A = 90, B = 80, C = 70, F = 'fail', } ``` 编译为: ```javascript 'use strict'; var Grade; (function (Grade) { Grade[(Grade['A'] = 90)] = 'A'; Grade[(Grade['B'] = 80)] = 'B'; Grade[(Grade['C'] = 70)] = 'C'; Grade['F'] = 'fail'; })(Grade || (Grade = {})); ``` 由此可见,对数字类型的枚举成员,可以从枚举值映射回枚举名称,但对字符串类型的枚举成员无法这样做。 ```typescript enum Grade { A = 90, B = 80, C = 70, F = 'fail', } const myGrade = Grade.A; console.log(Grade[myGrade]); // A console.log(Grade[90]); // A const failGrade = Grade.F; console.log(failGrade); // fail console.log(Grade[failGrade]); // 因为索引表达式的类型不是 'number',所以元素是隐式的 'any' 类型。 ``` ### 环境枚举 TypeScript 中的环境枚举是一种在声明文件 (*.d.ts) 中定义的枚举类型,没有关联的实现。它允许您定义一组命名常量,这些常量可以在不同文件中以类型安全的方式使用,而无需在每个文件中导入实现细节。 ### 计算成员和常量成员 在 TypeScript 中,计算成员是枚举的成员,其值在运行时计算,而常量成员是其值在编译时设置且在运行时无法更改的成员。常规枚举中允许使用计算成员,而常规枚举和常量枚举中都允许使用常量成员。 ```typescript // 常量成员 enum Color { Red = 1, Green = 5, Blue = Red + Green, } console.log(Color.Blue); // 6 编译时生成 ``` ```typescript // 计算成员 enum Color { Red = 1, Green = Math.pow(2, 2), Blue = Math.floor(Math.random() * 3) + 1, } console.log(Color.Blue); // 运行时生成的随机数 ``` 枚举由包含其成员类型的联合表示。每个成员的值可以通过常量或非常量表达式确定,拥有常量值的成员被分配字面量类型。为了说明这一点,请考虑类型 E 及其子类型 E.A、E.B 和 E.C 的声明。在本例中,E 表​​示联合 E.A | E.B | E.C 。 ```typescript const identity = (value: number) => value; enum E { A = 2 * 5, // 数字字面量 B = 'bar', // 字符串字面量 C = identity(42), // 不透明计算 } console.log(E.C); //42 ``` ================================================ FILE: website/src/content/docs/zh-cn/book/erased-structural-types.md ================================================ --- title: 擦除的结构类型 sidebar: order: 56 label: 56. 擦除的结构类型 --- 在 TypeScript 中,对象不必匹配特定的、精确的类型。例如,如果我们创建一个满足接口要求的对象,我们就可以在需要该接口的地方使用该对象,即使它们之间没有显式连接。例子: ```typescript type NameProp1 = { prop1: string; }; function log(x: NameProp1) { console.log(x.prop1); } const obj = { prop2: 123, prop1: 'Origin', }; log(obj); // 有效 ``` ================================================ FILE: website/src/content/docs/zh-cn/book/exhaustiveness-checking.md ================================================ --- title: 详尽性检查 sidebar: order: 26 label: 26. 详尽性检查 --- 详尽性检查是 TypeScript 中的一项功能,可确保在 `switch` 语句或 `if` 语句中处理可区分联合的所有可能情况。 ```typescript type Direction = 'up' | 'down'; const move = (direction: Direction) => { switch (direction) { case 'up': console.log('Moving up'); break; case 'down': console.log('Moving down'); break; default: const exhaustiveCheck: never = direction; console.log(exhaustiveCheck); // 这行永远不会被执行 } }; ``` 该 `never` 类型用于确保默认情况是详尽的,并且如果将新值添加到 Direction 类型而未在 switch 语句中进行处理,则 TypeScript 将引发错误。 ================================================ FILE: website/src/content/docs/zh-cn/book/exploring-the-type-system.md ================================================ --- title: 探索类型系统 sidebar: order: 9 label: 9. 探索类型系统 --- ### TypeScript 的语言服务 TypeScript 的语言服务, 也被称为 tsserver,提供了各种功能,例如错误报告、诊断、保存时编译、重命名、跳转到定义、补全列表、签名帮助等。 它主要由集成开发环境 (IDE) 使用来提供 IntelliSense 支持。它与 Visual Studio Code 无缝集成,并由 Conquer of Completion (Coc) 等工具使用。 开发人员可以利用专用 API 并创建自己的自定义语言服务插件来增强 TypeScript 编辑体验。这对于实现特殊的 linting 功能或启用自定义模板语言的自动完成特别有用。 现实世界中的自定义插件的一个示例是"typescript-styled-plugin",它为样式组件中的 CSS 属性提供语法错误报告和 IntelliSense 支持。 有关更多信息和快速入门指南,您可以参考 GitHub 上的官方 TypeScript Wiki: [https://github.com/microsoft/TypeScript/wiki/](https://github.com/microsoft/TypeScript/wiki/) ### 结构类型 TypeScript 基于结构类型系统。这意味着类型的兼容性和等效性由类型的实际结构或定义决定,而不是由其名称或声明位置决定,如 C# 或 C 等主要类型系统中那样。 TypeScript 的结构类型系统是基于 JavaScript 的动态 duck 类型系统在运行时的工作方式而设计的。 以下示例是有效的 TypeScript 代码。正如您所观察到的,"X"和"Y"具有相同的成员"a",尽管它们具有不同的声明名称。类型由其结构决定,在这种情况下,由于结构相同,因此它们是兼容且有效的。 ```typescript type X = { a: string; }; type Y = { a: string; }; const x: X = { a: 'a' }; const y: Y = x; // 有效 ``` ### TypeScript 的基本比较规则 TypeScript 比较过程是递归的,并在任何级别嵌套的类型上执行。 如果"Y"至少具有与"X"相同的成员,则类型"X"与"Y"兼容。 ```typescript type X = { a: string; }; const y = { a: 'A', b: 'B' }; // 有效, 至少它拥有相同的成员 X const r: X = y; ``` 函数参数按类型进行比较,而不是按名称进行比较: ```typescript type X = (a: number) => void; type Y = (a: number) => void; let x: X = (j: number) => undefined; let y: Y = (k: number) => undefined; y = x; // 有效 x = y; // 有效 ``` 函数返回类型必须相同: ```typescript type X = (a: number) => undefined; type Y = (a: number) => number; let x: X = (a: number) => undefined; let y: Y = (a: number) => 1; y = x; // 无效 x = y; // 无效 ``` 源函数的返回类型必须是目标函数的返回类型的子类型: ```typescript let x = () => ({ a: 'A' }); let y = () => ({ a: 'A', b: 'B' }); x = y; // 有效 y = x; // 无效,缺少 b 成员 ``` 允许丢弃函数参数,因为这是 JavaScript 中的常见做法,例如使用 "Array.prototype.map()": ```typescript [1, 2, 3].map((element, _index, _array) => element + 'x'); ``` 因此,以下类型声明是完全有效的: ```typescript type X = (a: number) => undefined; type Y = (a: number, b: number) => undefined; let x: X = (a: number) => undefined; let y: Y = (a: number) => undefined; // 缺少 b 参数 y = x; // 有效 ``` 源类型的任何附加可选参数都是有效的: ```typescript type X = (a: number, b?: number, c?: number) => undefined; type Y = (a: number) => undefined; let x: X = a => undefined; let y: Y = a => undefined; y = x; // 有效 x = y; // 有效 ``` 目标类型的任何可选参数在源类型中没有对应的参数都是有效的并且不是错误: ```typescript type X = (a: number) => undefined; type Y = (a: number, b?: number) => undefined; let x: X = a => undefined; let y: Y = a => undefined; y = x; // 有效 x = y; // 有效 ``` 其余参数被视为无限系列的可选参数: ```typescript type X = (a: number, ...rest: number[]) => undefined; let x: X = a => undefined; // 有效 ``` 如果重载签名与其实现签名兼容,则具有重载的函数有效: ```typescript function x(a: string): void; function x(a: string, b: number): void; function x(a: string, b?: number): void { console.log(a, b); } x('a'); // 有效 x('a', 1); // 有效 function y(a: string): void; // 无效, 不兼容重载的签名 function y(a: string, b: number): void; function y(a: string, b: number): void { console.log(a, b); } y('a'); y('a', 1); ``` 如果源参数和目标参数可赋值给超类型或子类型(Bivariance 双变),则函数参数比较成功。 ```typescript // 超类 class X { a: string; constructor(value: string) { this.a = value; } } // 子类 class Y extends X {} // 子类 class Z extends X {} type GetA = (x: X) => string; const getA: GetA = x => x.a; // 双变(Bivariance) 确实接收超类 console.log(getA(new X('x'))); // 有效 console.log(getA(new Y('Y'))); // 有效 console.log(getA(new Z('z'))); // 有效 ``` 枚举与数字具有可比性和有效性,反之亦然,但比较不同枚举类型的枚举值是无效的。 ```typescript enum X { A, B, } enum Y { A, B, C, } const xa: number = X.A; // 有效 const ya: Y = 0; // 有效 X.A === Y.A; // 无效 ``` 类的实例需要对其私有成员和受保护成员进行兼容性检查: ```typescript class X { public a: string; constructor(value: string) { this.a = value; } } class Y { private a: string; constructor(value: string) { this.a = value; } } let x: X = new Y('y'); // 无效 ``` 比较检查不考虑不同的继承层次结构,例如: ```typescript class X { public a: string; constructor(value: string) { this.a = value; } } class Y extends X { public a: string; constructor(value: string) { super(value); this.a = value; } } class Z { public a: string; constructor(value: string) { this.a = value; } } let x: X = new X('x'); let y: Y = new Y('y'); let z: Z = new Z('z'); x === y; // 有效 x === z; // 有效即使 z 来自不同的继承层次结构 ``` 泛型根据应用泛型参数后的结果类型使用其结构进行比较,仅将最终结果作为非泛型类型进行比较。 ```typescript interface X { a: T; } let x: X = { a: 1 }; let y: X = { a: 'a' }; x === y; // 无效,因为最终结构中使用了类型参数 ``` ```typescript interface X {} const x: X = 1; const y: X = 'a'; x === y; // 有效,因为最终结构中没有使用类型参数 ``` 当泛型未指定其类型参数时,所有未指定的参数都将被视为带有"any"的类型: ```typescript type X = (x: T) => T; type Y = (y: K) => K; let x: X = x => x; let y: Y = y => y; x = y; // 有效 ``` 记住: ```typescript let a: number = 1; let b: number = 2; a = b; // 有效,一切都可以赋值给自己 let c: any; c = 1; // 有效,所有类型都可以赋值给any let d: unknown; d = 1; // 有效,所有类型都可以赋值给unknown let e: unknown; let e1: unknown = e; // 有效, unknown只能赋值给自己和any let e2: any = e; // 有效 let e3: number = e; // 无效 let f: never; f = 1; // 无效, 所有类型不能赋值给never let g: void; let g1: any; g = 1; // 无效, void不可赋值给除"any"之外的任何内容或从任何内容赋值 g = g1; // 有效 ``` 请注意,当启用"strictNullChecks"时,"null"和"undefined"的处理方式与"void"类似;否则,它们类似于"never"。 ### 类型作为集合 在 TypeScript 中,类型是一组可能的值。该集合也称为类型的域。类型的每个值都可以被视为集合中的一个元素。类型建立了集合中的每个元素必须满足才能被视为该集合的成员的约束。TypeScript 的主要任务是检查并验证一组是否是另一组的子集。 TypeScript 支持各种类型的集合: | Set term | TypeScript | Notes | | -------- | ------------------------------- | ------------------------------------------------------------------------------ | | 空集 | never | "never" 包含除自身之外的任何类型 | | 单元素集 | undefined / null / literal type | | | 有限集 | boolean / union | | | 无限集 | string / number / object | | | 通用集 | any / unknown | 每个元素都是"any"的成员,每个集合都是它的子集/"unknown"是"any"的类型安全对应项 | 这里有几个例子: | TypScript | Set term | Example | | --------------------- | ---------------- | ---------------------------------------------------------------- | | never | ∅ (空集) | const x: never = 'x'; // 错误: 'string'类似不能赋值给'never'类型 | | | | | Literal type | 单元素集 | type X = 'X'; | | | | type Y = 7; | | | | | Value assignable to T | Value ∈ T (属于) | type XY = 'X' \| 'Y'; | | | | const x: XY = 'X'; | | | | | T1 assignable to T2 | T1 ⊆ T2 (子集) | type XY = 'X' \| 'Y'; | | | | const x: XY = 'X'; | | | | const j: XY = 'J'; // 类型'"J"' 不能赋值给 'XY' 类型. | | | | | | T1 extends T2 | T1 ⊆ T2 (子集) | type X = 'X' extends string ? true : false; | | | | | T1 \| T2 | T1 ∪ T2 (并集) | type XY = 'X' \| 'Y'; | | | | type JK = 1 \| 2; | | | | | T1 & T2 | T1 ∩ T2 (交集) | type X = \{ a: string \} | | | | type Y = \{ b: string \} | | | | type XY = X & Y | | | | const x: XY = \{ a: 'a', b: 'b' \} | | | | | unknown | 通用集 | const x: unknown = 1 | 并集 (T1 | T2) 创建一个更广泛的集合(两者): ```typescript type X = { a: string; }; type Y = { b: string; }; type XY = X | Y; const r: XY = { a: 'a', b: 'x' }; // 有效 ``` 交集(T1 & T2)创建一个更窄的集合(仅共享): ```typescript type X = { a: string; }; type Y = { a: string; b: string; }; type XY = X & Y; const r: XY = { a: 'a' }; // 无效 const j: XY = { a: 'a', b: 'b' }; // 有效 ``` 在这种情况下,关键字extends可以被视为"的子集"。它为类型设置约束。与泛型一起使用的扩展将泛型视为无限集,并将其限制为更具体的类型。请注意,这extends与 OOP 意义上的层次结构无关(TypScript 中没有这个概念)。TypeScript 使用集合并且没有严格的层次结构,事实上,如下面的示例所示,两种类型可以重叠,而不会成为另一种类型的子类型(TypScript 考虑对象的结构和形状)。 ```typescript interface X { a: string; } interface Y extends X { b: string; } interface Z extends Y { c: string; } const z: Z = { a: 'a', b: 'b', c: 'c' }; interface X1 { a: string; } interface Y1 { a: string; b: string; } interface Z1 { a: string; b: string; c: string; } const z1: Z1 = { a: 'a', b: 'b', c: 'c' }; const r: Z1 = z; // 有效 ``` ### 赋值类型:类型声明和类型断言 在 TypeScript 中可以通过不同的方式赋值类型: #### 类型声明 在下面的示例中,我们使用 x:X(":Type") 来声明变量 x 的类型。 ```typescript type X = { a: string; }; // 类型声明 const x: X = { a: 'a', }; ``` 如果变量不是指定的格式,TypeScript 将报告错误。例如: ```typescript type X = { a: string; }; const x: X = { a: 'a', b: 'b', // 错误: 对象字面量只能指定已知属性 }; ``` #### 类型断言 可以使用as关键字添加断言。这告诉编译器开发人员拥有有关类型的更多信息并消除可能发生的任何错误。 例如: ```typescript type X = { a: string; }; const x = { a: 'a', b: 'b', } as X; ``` 在上面的示例中,使用 as 关键字将对象 x 断言为类型 X。这通知 TypeScript 编译器该对象符合指定的类型,即使它具有类型定义中不存在的附加属性 b。 类型断言在需要指定更具体类型的情况下非常有用,尤其是在使用 DOM 时。例如: ```typescript const myInput = document.getElementById('my_input') as HTMLInputElement; ``` 此处,类型断言 HTMLInputElement 用于告诉 TypeScript getElementById 的结果应被视为 HTMLInputElement。类型断言还可以用于重新映射键,如下面使用模板文字的示例所示: ```typescript type J = { [Property in keyof Type as `prefix_${string & Property}`]: () => Type[Property]; }; type X = { a: string; b: number; }; type Y = J; ``` 在此示例中,类型 J 使用带有模板文字的映射类型来重新映射 Type 的键。它创建新属性,并在每个键上添加 prefix_ ,它们对应的值是返回原始属性值的函数。 值得注意的是,当使用类型断言时,TypeScript 不会执行多余的属性检查。因此,当预先知道对象的结构时,通常最好使用类型声明。 #### 非空断言 此断言是使用后缀表达式!运算符应用的,它告诉 TypeScript 值不能为 null 或未定义。 ```typescript let x: null | number; let y = x!; // number ``` #### 环境声明 环境声明是描述 JavaScript 代码类型的文件,它们的文件名格式为.d.ts.. 它们通常被导入并用于注释现有的 JavaScript 库或向项目中的现有 JS 文件添加类型。 许多常见的库类型可以在以下位置找到: [https://github.com/DefinitelyTyped/DefinitelyTyped/](https://github.com/DefinitelyTyped/DefinitelyTyped/) ```shell npm install --save-dev @types/library-name ``` 对于您定义的环境声明,您可以使用"三斜杠"引用导入: ```typescript /// ``` 即使在 JavaScript 文件中,您也可以通过 `// @ts-check` 使用环境声明。 `declare` 关键字可以为现有的 JavaScript 代码启用类型定义,而无需导入它,作为来自另一个文件或全局的类型的占位符。 ### 属性检测和多余属性检测 TypeScript 基于结构类型系统,但过多的属性检查是 TypeScript 的一个属性,它允许它检查对象是否具有类型中指定的确切属性。 例如,在将对象字面量赋值给变量或将它们作为参数传递给函数的多余属性时,会执行多余属性检查。 ```typescript type X = { a: string; }; const y = { a: 'a', b: 'b' }; const x: X = y; // 有效,因为结构类型 const w: X = { a: 'a', b: 'b' }; // 无效,因为多余属性检测 ``` ### 弱类型 当一个类型只包含一组全可选属性时,该类型被认为是弱类型: ```typescript type X = { a?: string; b?: string; }; ``` 当没有重叠时,TypeScript 认为将任何内容赋值给弱类型是错误的,例如,以下会引发错误: ```typescript type Options = { a?: string; b?: string; }; const fn = (options: Options) => undefined; fn({ c: 'c' }); // 无效 ``` 尽管不推荐,但如果需要,可以使用类型断言绕过此检查: ```typescript type Options = { a?: string; b?: string; }; const fn = (options: Options) => undefined; fn({ c: 'c' } as Options); // 有效 ``` 或者通过将unknown索引签名添加到弱类型: ```typescript type Options = { [prop: string]: unknown; a?: string; b?: string; }; const fn = (options: Options) => undefined; fn({ c: 'c' }); // 有效 ``` ### 严格的对象字面量检测 (Freshness) 严格的对象字面量检查(有时称为“新鲜度”)是 TypeScript 中的一项功能,有助于捕获多余或拼写错误的属性,否则这些属性在正常结构类型检查中会被忽视。 创建对象字面量时,TypeScript 编译器认为它是“新鲜的”。 如果将对象字面量分配给变量或作为参数传递,并且对象字面量指定目标类型中不存在的属性,则 TypeScript 将引发错误。 然而,当扩展对象文字或使用类型断言时,“新鲜感”就会消失。 下面举一些例子来说明: ```typescript type X = { a: string }; type Y = { a: string; b: string }; let x: X; x = { a: 'a', b: 'b' }; // 严格的对象字面量检查:无效的赋值 var y: Y; y = { a: 'a', bx: 'bx' }; // 严格的对象字面量检查:无效的赋值 const fn = (x: X) => console.log(x.a); fn(x); fn(y); // 类型加宽:没有错误, 结构类型兼容 fn({ a: 'a', bx: 'b' }); // 严格的对象字面量检查:无效的参数 let c: X = { a: 'a' }; let d: Y = { a: 'a', b: '' }; c = d; // 类型加宽:没有严格的对象字面量检查 ``` ### 类型推断 当在以下期间未提供注释时,TypeScript 可以推断类型: * 变量初始化 * 成员初始化。 * 设置参数的默认值。 * 函数返回类型。 例如: ```typescript let x = 'x'; // 推断的类型是 string ``` TypeScript 编译器分析值或表达式并根据可用信息确定其类型。 ### 更高级的推断 当在类型推断中使用多个表达式时,TypeScript 会查找"最佳常见类型"。例如: ```typescript let x = [1, 'x', 1, null]; // 类型推断为: (string | number | null)[] ``` 如果编译器找不到最佳通用类型,它将返回联合类型。例如: ```typescript let x = [new RegExp('x'), new Date()]; // 类型推断为: (RegExp | Date)[] ``` TypeScript 利用基于变量位置的"上下文类型"来推断类型。在下面的示例中,编译器知道它的e类型是MouseEvent,因为在lib.d.ts 文件中定义了click事件类型,该文件包含各种常见 JavaScript 构造和 DOM 的环境声明: ```typescript window.addEventListener('click', function (e) {}); // e 的类型被推断为 MouseEvent ``` ### 类型加宽 类型加宽是 TypeScript 将类型分配给未提供类型注释时初始化的变量的过程。它允许从窄到宽的类型,但反之则不然。在以下示例中: ```typescript let x = 'x'; // TypeScript 推断为字符串,一种宽类型 let y: 'y' | 'x' = 'y'; // y 类型是字面量类型的联合 y = x; // 无效,字符串不可分配给类型 'x' | 'y'。 ``` TypeScript根据初始化期间提供的单个值(`x`),将 `string` 赋予给 `x`,这是一个扩展的示例。 TypeScript 提供了控制加宽过程的方法,例如使用"const"。 ### 常量 在声明变量时使用 `const` 关键字会导致 TypeScript 中的类型推断范围更窄。 For example: ```typescript const x = 'x'; // TypeScript 将 'x' 推断为带有 'const'(不可变)的字符串字面量,但将其扩展为带有 'let'(可重新赋值)的 'string'。 let y: 'y' | 'x' = 'y'; y = x; // 有效: x的类型推断为 'x' ``` 通过使用 const 声明变量 x,其类型被缩小为特定的文字值"x"。由于 x 的类型被缩小,因此可以将其赋值给变量 y 而不会出现任何错误。可以推断类型的原因是因为 const 变量无法重新分配,因此它们的类型可以缩小到特定的文字类型,在本例中为字面量类型"x"。 #### 类型参数的 const 修饰符 从 TypeScript 5.0 版本开始,可以 `const` 在泛型类型参数上指定属性。这可以推断出最精确的类型。让我们看一个不使用 `const` 的示例: ```typescript function identity(value: T) { // 这里没有const return value; } const values = identity({ a: 'a', b: 'b' }); // 类型推断为: { a: string; b: string; } ``` 正如您所看到的,属性a和b是通过 类型推断出来的string 。 现在,让我们看看 `const` 版本的差异: ```typescript function identity(value: T) { // 对类型参数使用 const 修饰符 return value; } const values = identity({ a: 'a', b: 'b' }); // 类型推断为: { a: "a"; b: "b"; } ``` 现在我们可以看到属性 `a` 和 `b` 被推断为const,因此 `a` 和 `b`被视为字符串文字而不仅仅是 `string` 类型。 ### 常量断言 此功能允许您根据变量的初始化值声明具有更精确的文字类型的变量,这向编译器表明该值应被视为不可变文字。 这里有一些例子: 在单个属性上: ```typescript const v = { x: 3 as const, }; v.x = 3; ``` 在整个对象上: ```typescript const v = { x: 1, y: 2, } as const; ``` 这在定义元组的类型时特别有用: ```typescript const x = [1, 2, 3]; // number[] const y = [1, 2, 3] as const; // 只读数组 [1, 2, 3] ``` ### 显式类型注释 我们可以具体地传递一个类型,在下面的示例中,属性x的类型是number: ```typescript const v = { x: 1, // 推断类型: number (加宽了) }; v.x = 3; // 有效 ``` 我们可以通过使用字面量类型的联合使类型注释更加具体: ```typescript const v: { x: 1 | 2 | 3 } = { x: 1, // x 现在是字面量的联合类型: 1 | 2 | 3 }; v.x = 3; // 有效 v.x = 100; // 无效的 ``` ### 类型缩小 类型缩小是 TypeScript 中的一个过程,其中一般类型缩小为更具体的类型。当 TypeScript 分析代码并确定某些条件或操作可以细化类型信息时,就会发生这种情况。 缩小类型可以通过不同的方式发生,包括: #### 条件 通过使用条件语句(比如 `if` 或 `switch`),TypeScript 可以根据条件的结果缩小类型范围。例如: ```typescript let x: number | undefined = 10; if (x !== undefined) { x += 100; // 由于条件判断,类型被缩小为 number } ``` #### 抛错或者返回 抛出错误或从分支提前返回可用于帮助 TypeScript 缩小类型范围。例如: ```typescript let x: number | undefined = 10; if (x === undefined) { throw 'error'; } x += 100; ``` 在 TypeScript 中缩小类型范围的其他方法包括: * `instanceof` 操作: 用于检查对象是否是特定类的实例。 * `in` 操作: 用于检查对象中是否存在属性。 * `typeof` 操作: 用于在运行时检查值的类型。 * 内部函数,比如: `Array.isArray()`: 用于检查值是否为数组。 #### 可区分联合 使用"可区分联合"是 TypeScript 中的一种模式,其中向对象添加显式"标签"以区分联合内的不同类型。该模式也称为"标记联合"。在以下示例中,"tag"由属性"type"表示: ```typescript type A = { type: 'type_a'; value: number }; type B = { type: 'type_b'; value: string }; const x = (input: A | B): string | number => { switch (input.type) { case 'type_a': return input.value + 100; // 类型为 A case 'type_b': return input.value + 'extra'; // 类型为 B } }; ``` #### 用户定义的类型保护 在 TypeScript 无法确定类型的情况下,可以编写一个称为"用户定义类型保护"的辅助函数。在下面的示例中,我们将在应用某些过滤后利用类型谓词来缩小类型范围: ```typescript const data = ['a', null, 'c', 'd', null, 'f']; const r1 = data.filter(x => x != null); // 类型为 (string | null)[], TypeScript 不能准确推断类型 const isValid = (item: string | null): item is string => item !== null; // 自定义类型保护 const r2 = data.filter(isValid); // 类型现在为 string[], 通过使用断言类型保护,我们能够缩小类型 ``` ================================================ FILE: website/src/content/docs/zh-cn/book/extending-types.md ================================================ --- title: 扩展类型 sidebar: order: 15 label: 15. 扩展类型 --- 可以扩展 `interface`(从另一种类型复制成员): ```typescript interface X { a: string; } interface Y extends X { b: string; } ``` 还可以从多种 `interface` 进行扩展: ```typescript interface A { a: string; } interface B { b: string; } interface Y extends A, B { y: string; } ``` 该 `extends` 关键字仅适用于 `interface`,因为 `type` 使用交集: ```typescript type A = { a: number; }; type B = { b: number; }; type C = A & B; ``` 可以使用 `interface` 来扩展类 `type`,但反之则不然: ```typescript type A = { a: string; }; interface B extends A { b: string; } ``` ================================================ FILE: website/src/content/docs/zh-cn/book/fixed-length-tuple.md ================================================ --- title: 固定长度元组 sidebar: order: 30 label: 30. 固定长度元组 --- 固定长度元组是一种特定类型的元组,它强制执行特定类型的固定数量的元素,并且一旦定义元组就不允许对其长度进行任何修改。 当您需要表示具有特定数量的元素和特定类型的值的集合,并且您希望确保元组的长度和类型不会无意中更改时,固定长度元组非常有用。 ```typescript const x = [10, 'hello'] as const; x.push(2); // 错误 ``` ================================================ FILE: website/src/content/docs/zh-cn/book/generics.md ================================================ --- title: 泛型 sidebar: order: 55 label: 55. 泛型 --- 泛型允许您创建可与多种类型一起使用的可重用组件和函数。使用泛型,您可以参数化类型、函数和接口,从而允许它们对不同类型进行操作,而无需事先显式指定它们。 泛型允许您使代码更加灵活和可重用。 ### 泛型类型 要定义泛型类型,可以使用尖括号 (`<>`) 来指定类型参数,例如: ```typescript function identity(arg: T): T { return arg; } const a = identity('x'); const b = identity(123); const getLen = (data: ReadonlyArray) => data.length; const len = getLen([1, 2, 3]); ``` ### 泛型类 泛型也可以应用于类,这样它们就可以通过使用类型参数来处理多种类型。这对于创建可重用的类定义非常有用,这些定义可以在保持类型安全的同时对不同的数据类型进行操作。 ```typescript class Container { private item: T; constructor(item: T) { this.item = item; } getItem(): T { return this.item; } } const numberContainer = new Container(123); console.log(numberContainer.getItem()); // 123 const stringContainer = new Container('hello'); console.log(stringContainer.getItem()); // hello ``` ### 泛型约束 可以使用关键字 `extends` 后跟类型参数必须满足的类型或接口来约束泛型参数。 在下面的示例中,T 必须正确包含 `length` 才能有效: ```typescript const printLen = (value: T): void => { console.log(value.length); }; printLen('Hello'); // 5 printLen([1, 2, 3]); // 3 printLen({ length: 10 }); // 10 printLen(123); // 无效 ``` 3.4 RC 版中引入的泛型的一个有趣功能是高阶函数类型推断,它引入了传播泛型类型参数: ```typescript declare function pipe
( ab: (...args: A) => B, bc: (b: B) => C ): (...args: A) => C; declare function list(a: T): T[]; declare function box(x: V): { value: V }; const listBox = pipe(list, box); // (a: T) => { value: T[] } const boxList = pipe(box, list); // (x: V) => { value: V }[] ``` 此功能允许更轻松地键入安全的无点风格编程,这在函数式编程中很常见。 ### 泛型上下文缩小 泛型上下文缩小是 TypeScript 中的机制,允许编译器根据使用泛型参数的上下文来缩小泛型参数的类型,在条件语句中使用泛型类型时非常有用: ```typescript function process(value: T): void { if (typeof value === 'string') { // Value 的类型被缩小到 'string' 类型 console.log(value.length); } else if (typeof value === 'number') { // Value 的类型被缩小到 'number' 类型 console.log(value.toFixed(2)); } } process('hello'); // 5 process(3.14159); // 3.14 ``` ================================================ FILE: website/src/content/docs/zh-cn/book/getting-started-with-typescript.md ================================================ --- title: TypeScript 入门 sidebar: order: 8 label: 8. TypeScript 入门 --- ### 安装 Visual Studio Code 为 TypeScript 语言提供了出色的支持,但不包含 TypeScript 编译器。要安装 TypeScript 编译器,您可以使用包管理器,例如 npm 或yarn: ```shell npm install typescript --save-dev ``` 或者 ```shell yarn add typescript --dev ``` 确保提交生成的锁定文件,以确保每个团队成员使用相同版本的 TypeScript。 要运行TypeScript编译器,可以使用以下命令 ```shell npx tsc ``` 或者 ```shell yarn tsc ``` 建议按项目安装 TypeScript,而不是全局安装,因为它提供了更可预测的构建过程。但是,对于一次性情况,您可以使用以下命令: ```shell npx tsc ``` 或者安装到全局: ```shell npm install -g typescript ``` 如果您使用的是 Microsoft Visual Studio,则可以在 NuGet 中为 MSBuild 项目获取作为包的 TypeScript。在 NuGet 包管理器控制台中,运行以下命令: ```shell Install-Package Microsoft.TypeScript.MSBuild ``` 在 TypeScript 安装过程中,会安装两个可执行文件:"tsc"作为 TypeScript 编译器,"tsserver"作为 TypeScript 独立服务器。独立服务器包含编译器和语言服务,编辑器和 IDE 可以利用它们来提供智能代码补全。 此外,还有几种兼容 TypeScript 的转译器可用,例如 Babel(通过插件)或 swc。这些转译器可用于将 TypeScript 代码转换为其他目标语言或版本。 ### 配置 可以使用 tsc CLI 选项或利用位于项目根目录中名为 tsconfig.json 的专用配置文件来配置 TypeScript。 要生成预填充推荐设置的 tsconfig.json 文件,您可以使用以下命令: ```shell tsc --init ``` 在本地执行tsc命令时,TypeScript 将使用最近的 tsconfig.json 文件中指定的配置来编译代码。 以下是使用默认设置运行的 CLI 命令的一些示例: ```shell tsc main.ts // 将一个特定的文件 (main.ts) 编译成 JavaScript tsc src/*.ts // 将 'src' 文件夹下任意的 .ts 文件编译成 JavaScript tsc app.ts util.ts --outfile index.js // 将 2 个 TypeScript 文件 (app.ts 和 util.ts) 编译成 1 个 JavaScript 文件 (index.js) ``` ### TypeScript 的配置文件 tsconfig.json 文件用于配置 TypeScript 编译器 (tsc)。通常,它与文件一起添加到项目的根目录中package.json。 注意: * tsconfig.json 即使是 json 格式也接受注释。 * 建议使用此配置文件而不是命令行选项。 在以下链接中,您可以找到完整的文档及其配置示例: [https://www.typescriptlang.org/tsconfig](https://www.typescriptlang.org/tsconfig) [https://www.typescriptlang.org/tsconfig/](https://www.typescriptlang.org/tsconfig/) 以下列出了常见且有用的配置: #### target "target"属性用于指定 TypeScript 应发出/编译到哪个版本的 JavaScript ECMAScript 版本。对于现代浏览器,ES6是一个不错的选择,对于较旧的浏览器,建议使用ES5。 #### lib "lib"属性用于指定编译时要包含哪些库文件。TypeScript 自动包含"目标"属性中指定功能的 API,但可以根据特定需求省略或选择特定库。例如,如果您正在开发服务器项目,则可以排除"DOM"库,该库仅在浏览器环境中有用。 #### strict "strict"属性可以提供更强有力的保证并增强类型安全性。建议始终将此属性包含在项目的 tsconfig.json 文件中。启用"strict"属性允许 TypeScript : * 触发每个源文件的代码使用"use strict"。 * 在类型检查过程中考虑"null"和"undefined" * 当不存在类型注释时禁用"any"类型的使用。 * 在使用"this"表达式时引发错误,否则"this"会被视为任意类型。 #### module "module"属性设置编译程序支持的模块系统。在运行时,模块加载器用于根据指定的模块系统定位并执行依赖项。 JavaScript 中最常见的模块加载器是用于服务器端应用程序的 Node.js 的CommonJS和用于基于浏览器的 Web 应用程序中的 AMD 模块的 RequireJS。 TypeScript 可以为各种模块系统生成代码,包括 UMD、System、ESNext、ES2015/ES6 和 ES2020。 注意:应根据目标环境和该环境中可用的模块加载机制来选择模块系统。 #### moduleResolution "moduleResolution"属性指定模块解析策略。对现代TypeScript代码使用"node","classic"仅用于旧版本的 TypeScript(1.6 之前)。 #### esModuleInterop "esModuleInterop"属性允许从未使用"default"属性导出的 CommonJS 模块导入默认值,此属性提供了一个兼容以确保生成的 JavaScript 的兼容性。启用此选项后,我们可以使用 `import MyLibrary from "my-library"` 而不是 `import * as MyLibrary from "my-library"`。 #### jsx "jsx"属性仅适用于 ReactJS 中使用的 .tsx 文件,并控制 JSX 构造如何编译为 JavaScript。一个常见的选项是"preserve",它将编译为 .jsx 文件,保持 JSX 不变,以便可以将其传递给 Babel 等不同工具进行进一步转换。 #### skipLibCheck "skipLibCheck"属性将阻止 TypeScript 对整个导入的第三方包进行类型检查。此属性将减少项目的编译时间。TypeScript 仍会根据这些包提供的类型定义检查您的代码。 #### files "files"属性向编译器指示必须始终包含在程序中的文件列表。 #### include "include"属性向编译器指示我们想要包含的文件列表。此属性允许类似 glob 的模式,例如 "\*_" 表示任何子目录,"_" 表示任何文件名,"?" 表示可选字符。 #### exclude "exclude"属性向编译器指示不应包含在编译中的文件列表。这可以包括"node_modules"等文件或测试文件 注意:tsconfig.json 允许注释。 ### importHelpers TypeScript 在为某些高级或低级 JavaScript 功能生成代码时使用帮助程序代码。 默认情况下,这些助手会在使用它们的文件中复制。 `importHelpers` 选项从 `tslib` 模块导入这些帮助器,从而使 JavaScript 输出更加高效。 ### 迁移到 TypeScript 的建议 对于大型项目,建议采用逐渐过渡的方式,其中 TypeScript 和 JavaScript 代码最初共存。只有小型项目才能一次性迁移到 TypeScript。 此转变的第一步是将 TypeScript 引入构建链过程。这可以通过使用"allowJs"编译器选项来完成,该选项允许 .ts 和 .tsx 文件与现有 JavaScript 文件共存。由于当 TypeScript 无法从 JavaScript 文件推断类型时,它会回退到变量的"any"类型,因此建议在迁移开始时在编译器选项中禁用"noImplicitAny"。 第二步是确保您的 JavaScript 测试与 TypeScript 文件一起工作,以便您可以在转换每个模块时运行测试。如果您正在使用 Jest,请考虑使用ts-jest,它允许您使用 Jest 测试 TypeScript 项目。 第三步是在项目中包含第三方库的类型声明。 这些声明可以第三方库的类型声明文件或专门的声明包中找到,你能通过 [https://www.typescriptlang.org/dt/search](https://www.typescriptlang.org/dt/search) 搜索并安装它们。: ```shell npm install --save-dev @types/package-name ``` 或者 ```shell yarn add --dev @types/package-name ``` 第四步是使用自下而上的方法逐个模块地迁移,遵循从叶开始的依赖关系图。这个想法是开始转换不依赖于其他模块的模块。要可视化依赖关系图,您可以使用该madge工具。 有一些对于转换成 TypeScript 比较友好的模块(外部 API 或规范相关的实用函数和代码),比如Swagger、GraphQL 或 JSONSchema 自动生成 TypeScript 类型定义,并使用在您的项目中。 当没有可用的规范或官方架构时,您可以从原始数据生成类型,例如服务器返回的 JSON。但是,建议从规范而不是数据生成类型,以避免丢失边缘情况。 在迁移过程中,不要进行代码重构,而只专注于向模块添加类型。 第五步是启用"noImplicitAny",这将强制所有类型都是已知和定义的,从而为您的项目提供更好的 TypeScript 体验。 在迁移过程中,您可以使用该@ts-check指令,该指令在 JavaScript 文件中启用 TypeScript 类型检查。该指令提供了宽松版本的类型检查,最初可用于识别 JavaScript 文件中的问题。当@ts-check包含在文件中时,TypeScript 将尝试使用 JSDoc 风格的注释来推断定义。但是,仅在迁移的早期阶段考虑使用 JSDoc 注释。 考虑在你的tsconfig.json文件中将 `noEmitOnError` 设置为 false,即使报告错误,这也将允许您输出 JavaScript 源代码。 ================================================ FILE: website/src/content/docs/zh-cn/book/index-signatures.md ================================================ --- title: 索引签名 sidebar: order: 14 label: 14. 索引签名 --- 在 TypeScript 中,我们可以使用 `string` 、`number` 和 `symbol` 作为索引签名: ```typescript type K = { [name: string | number]: string; }; const k: K = { x: 'x', 1: 'b' }; console.log(k['x']); console.log(k[1]); console.log(k['1']); // 同 k[1] 的结果相同 ``` 请注意,JavaScript 会自动将 `number` 的索引转换相同值的 'string'索引, 比如 `k[1]` 和 `k["1"]` 返回相同值。 ================================================ FILE: website/src/content/docs/zh-cn/book/infer-type-inference-in-conditional-types.md ================================================ --- title: infer 条件类型中的类型推断 sidebar: order: 41 label: 41. infer 条件类型中的类型推断 --- `infer` 关键字在条件类型中使用,用于从依赖于泛型参数的类型中推断(提取)泛型参数的类型。这允许您编写更灵活且可重用的类型定义。 ```typescript type ElementType = T extends (infer U)[] ? U : never; type Numbers = ElementType; // number type Strings = ElementType; // string ``` ================================================ FILE: website/src/content/docs/zh-cn/book/interface-and-type.md ================================================ --- title: 接口及类型 sidebar: order: 48 label: 48. 接口及类型 --- ### 通用语法 在 TypeScript 中,接口定义对象的结构,指定对象必须具有的属性或方法的名称和类型。在 TypeScript 中定义接口的常用语法如下: ```typescript interface InterfaceName { property1: Type1; // ... method1(arg1: ArgType1, arg2: ArgType2): ReturnType; // ... } ``` 类型定义也类似: ```typescript type TypeName = { property1: Type1; // ... method1(arg1: ArgType1, arg2: ArgType2): ReturnType; // ... }; ``` `interface InterfaceName` 或者 `type TypeName`: 定义接口的名称。 `property1`: `Type1`: 指定接口的属性及其相应的类型。可以定义多个属性,每个属性用分号分隔。 `method1(arg1: ArgType1, arg2: ArgType2): ReturnType;`: 指定接口的方法。方法用其名称进行定义,后跟括号中的参数列表和返回类型。可以定义多个方法,每个方法用分号分隔。 接口示例: ```typescript interface Person { name: string; age: number; greet(): void; } ``` 类型示例: ```typescript type TypeName = { property1: string; method1(arg1: string, arg2: string): string; }; ``` 在 TypeScript 中,类型用于定义数据的形状并强制执行类型检查。在 TypeScript 中定义类型有几种常见的语法,具体取决于具体的用例。这里有些例子: ### 基本类型 ```typescript let myNumber: number = 123; // 数字类型 let myBoolean: boolean = true; // 布尔类型 let myArray: string[] = ['a', 'b']; // 字符串数组 let myTuple: [string, number] = ['a', 123]; // 元组 ``` ### 对象和接口 ```typescript const x: { name: string; age: number } = { name: 'Simon', age: 7 }; ``` ### 并集和交集类型 ```typescript type MyType = string | number; // 并集 let myUnion: MyType = 'hello'; // 可以是字符串 myUnion = 123; // 或者是一个数字 type TypeA = { name: string }; type TypeB = { age: number }; type CombinedType = TypeA & TypeB; // 交集 let myCombined: CombinedType = { name: 'John', age: 25 }; // 对象同时有name和age属性 ``` ================================================ FILE: website/src/content/docs/zh-cn/book/intersection-types.md ================================================ --- title: 交集类型 sidebar: order: 32 label: 32. 交集类型 --- 交集类型是表示具有两种或多种类型的所有属性的值的类型。交叉类型在每种类型之间使用 & 符号表示。 ```typescript type X = { a: string; }; type Y = { b: string; }; type J = X & Y; // 交集 const j: J = { a: 'a', b: 'b', }; ``` ================================================ FILE: website/src/content/docs/zh-cn/book/introduction.md ================================================ --- title: 介绍 sidebar: order: 5 label: 5. 介绍 --- 欢迎来到简洁的TypeScript之书!本指南为您提供有效 TypeScript 开发的基本知识和实践技能。发现编写干净、健壮的代码的关键概念和技术。无论您是初学者还是经验丰富的开发人员,本书都可以作为在项目中利用 TypeScript 强大功能的综合指南和便捷参考。 本书涵盖了 TypeScript 5.2。 ================================================ FILE: website/src/content/docs/zh-cn/book/literal-inference.md ================================================ --- title: 字面量推断 sidebar: order: 17 label: 17. 字面量推断 --- 字面量推断是 TypeScript 中的一项功能,允许根据变量或参数的值推断其类型。 在下面的示例中,我们可以看到 TypeScript 认为x文字类型是因为该值以后不能随时更改,而y被推断为字符串,因为它以后可以随时修改。 ```typescript const x = 'x'; // x 为字面量类型, 因为值不能改变 let y = 'y'; // string, 我们能改变这个值 ``` 在下面的示例中,我们可以看到 `o.x` 被推断为 `string`(而不是字面量的a),因为 TypeScript 认为该值可以在以后随时更改。 ```typescript type X = 'a' | 'b'; let o = { x: 'a', // 这是一个更宽的 string }; const fn = (x: X) => `${x}-foo`; console.log(fn(o.x)); // 'string' 类型的参数不能赋值给 'X' 类型的参数 ``` 正如你所看到的代码在传递 `o.x` 给 `fn` 作为一个狭窄类型时,抛出了一个错误。 我们能通过使用 `const` 或者 `X` 来借助类型推断解决这个问题: ```typescript let o = { x: 'a' as const, }; ``` or: ```typescript let o = { x: 'a' as X, }; ``` ================================================ FILE: website/src/content/docs/zh-cn/book/literal-types.md ================================================ --- title: 字面量类型 sidebar: order: 16 label: 16. 字面量类型 --- 文字类型是来自集体类型的单个元素集,它定义了一个非常精确的值,即 JavaScript 原始数据。 TypeScript 中的文字类型是数字、字符串和布尔值。 示例如下: ```typescript const a = 'a'; // 字符串字面量类型 const b = 1; // 数字字面量类型 const c = true; // 布尔字面量类型 ``` 在以下示例中,您可以看到联合类型别名。`O` 仅包含指定的值,其他字符串均无效: ```typescript type O = 'a' | 'b' | 'c'; ``` ================================================ FILE: website/src/content/docs/zh-cn/book/mapped-type-modifiers.md ================================================ --- title: 映射类型修饰符 sidebar: order: 38 label: 38. 映射类型修饰符 --- TypeScript 中的映射类型修饰符支持对现有类型中的属性进行转换: * `readonly` 或 `+readonly`:这会将映射类型中的属性呈现为只读。 * `-readonly`:这允许映射类型中的属性是可变的。 * `?`:这将映射类型中的属性指定为可选。 例子: ```typescript type ReadOnly = { readonly [P in keyof T]: T[P] }; // 所有属性标记为只读 type Mutable = { -readonly [P in keyof T]: T[P] }; // 所有标记为可变的属性 type MyPartial = { [P in keyof T]?: T[P] }; // 所有标记为可选的属性 ```` ================================================ FILE: website/src/content/docs/zh-cn/book/mapped-types.md ================================================ --- title: 映射类型 sidebar: order: 37 label: 37. 映射类型 --- TypeScript 中的映射类型允许您通过使用映射函数转换每个属性来基于现有类型创建新类型。通过映射现有类型,您可以创建以不同格式表示相同信息的新类型。要创建映射类型,您可以使用运算符访问现有类型的属性 `keyof` ,然后更改它们以生成新类型。在以下示例中: ```typescript type MyMappedType = { [P in keyof T]: T[P][]; }; type MyType = { foo: string; bar: number; }; type MyNewType = MyMappedType; const x: MyNewType = { foo: ['hello', 'world'], bar: [1, 2, 3], }; ``` 我们定义 MyMappedType 来映射 T 的属性,创建一个新类型,其中每个属性都是其原始类型的数组。使用它,我们创建 MyNewType 来表示与 MyType 相同的信息,但每个属性都是一个数组。 ================================================ FILE: website/src/content/docs/zh-cn/book/merging-and-extension.md ================================================ --- title: 合并与扩展 sidebar: order: 52 label: 52. 合并与扩展 --- 合并和扩展是指与使用类型和接口相关的两个不同概念。 合并允许您将多个同名声明合并到一个定义中,例如,当您多次定义同名接口时: ```typescript interface X { a: string; } interface X { b: number; } const person: X = { a: 'a', b: 7, }; ``` 扩展是指扩展或继承现有类型或接口以创建新类型或接口的能力。它是一种向现有类型添加附加属性或方法而不修改其原始定义的机制。例子: ```typescript interface Animal { name: string; eat(): void; } interface Bird extends Animal { sing(): void; } const dog: Bird = { name: 'Bird 1', eat() { console.log('Eating'); }, sing() { console.log('Singing'); }, }; ``` ================================================ FILE: website/src/content/docs/zh-cn/book/named-tuple-type-labeled.md ================================================ --- title: 命名元组类型(已标记) sidebar: order: 29 label: 29. 命名元组类型(已标记) --- 元组类型可以包含每个元素的可选标签或名称。 这些标签用于提高可读性和工具帮助,不会影响您可以使用它们执行的操作。 ```typescript type T = string; type Tuple1 = [T, T]; type Tuple2 = [a: T, b: T]; type Tuple3 = [a: T, T]; // 命名元组加匿名元组 ``` ================================================ FILE: website/src/content/docs/zh-cn/book/namespacing.md ================================================ --- title: 命名空间 sidebar: order: 57 label: 57. 命名空间 --- 在 TypeScript 中,命名空间用于将代码组织到逻辑容器中,防止命名冲突并提供一种将相关代码分组在一起的方法。使用关键字 `export` 允许在"外部"模块中访问名称空间。 ```typescript export namespace MyNamespace { export interface MyInterface1 { prop1: boolean; } export interface MyInterface2 { prop2: string; } } const a: MyNamespace.MyInterface1 = { prop1: true, }; ``` ================================================ FILE: website/src/content/docs/zh-cn/book/narrowing.md ================================================ --- title: 缩小范围 sidebar: order: 20 label: 20. 缩小范围 --- TypeScript 缩小范围是细化条件块内变量类型的过程。这在使用联合类型时很有用,其中一个变量可以有多个类型。 TypeScript 可识别多种缩小类型范围的方法: ### typeof 类型保护 typeof 类型保护是 TypeScript 中的一种特定类型保护,它根据变量的内置 JavaScript 类型检查变量的类型。 ```typescript const fn = (x: number | string) => { if (typeof x === 'number') { return x + 1; // x 是数字 } return -1; }; ``` ### 真实性缩小 TypeScript 中的真实性缩小是通过检查变量是真还是假来相应地缩小其类型来实现的。 ```typescript const toUpperCase = (name: string | null) => { if (name) { return name.toUpperCase(); } else { return null; } }; ``` ### 相等缩小 TypeScript 中的相等缩小通过检查变量是否等于特定值来相应缩小其类型。 它与`switch`语句和等号运算符(例如`===`、`!==`、`==`和`!=`)结合使用来缩小类型范围。 ```typescript const checkStatus = (status: 'success' | 'error') => { switch (status) { case 'success': return true; case 'error': return null; } }; ``` ### In运算符缩小 TypeScript 中的 `in` 运算符缩小范围是一种根据变量类型中是否存在属性来缩小变量类型的方法。 ```typescript type Dog = { name: string; breed: string; }; type Cat = { name: string; likesCream: boolean; }; const getAnimalType = (pet: Dog | Cat) => { if ('breed' in pet) { return 'dog'; } else { return 'cat'; } }; ``` ### instanceof 缩小 TypeScript 中的 `instanceof` 运算符缩小是一种根据变量的构造函数缩小变量类型的方法,方法是检查对象是否是某个类或接口的实例。 ```typescript class Square { constructor(public width: number) {} } class Rectangle { constructor( public width: number, public height: number ) {} } function area(shape: Square | Rectangle) { if (shape instanceof Square) { return shape.width * shape.width; } else { return shape.width * shape.height; } } const square = new Square(5); const rectangle = new Rectangle(5, 10); console.log(area(square)); // 25 console.log(area(rectangle)); // 50 ``` ================================================ FILE: website/src/content/docs/zh-cn/book/never-type.md ================================================ --- title: Never类型 sidebar: order: 47 label: 47. Never类型 --- `never` 类型表示从未出现过的值。它用于表示从不返回或抛出错误的函数或表达式。 例如无限循环: ```typescript const infiniteLoop = (): never => { while (true) { // 做点什么 } }; ``` 抛出错误: ```typescript const throwError = (message: string): never => { throw new Error(message); }; ``` `never` 类型对于确保类型安全和捕获代码中的潜在错误很有用。当与其他类型和控制流语句结合使用时,它可以帮助 TypeScript 分析和推断更精确的类型,例如: ```typescript type Direction = 'up' | 'down'; const move = (direction: Direction): void => { switch (direction) { case 'up': // 向上移动 break; case 'down': // 向下移动 break; default: const exhaustiveCheck: never = direction; throw new Error(`Unhandled direction: ${exhaustiveCheck}`); } }; ``` ================================================ FILE: website/src/content/docs/zh-cn/book/object-types.md ================================================ --- title: 对象类型 sidebar: order: 27 label: 27. 对象类型 --- 在 TypeScript 中,对象类型描述对象的形状。它们指定对象属性的名称和类型,以及这些属性是必需的还是可选的。 在 TypeScript 中,您可以通过两种主要方式定义对象类型: 通过指定对象属性的名称、类型和可选性来定义对象的形状的接口。 ```typescript interface User { name: string; age: number; email?: string; } ``` 类型别名与接口类似,定义了对象的形状。但是,它还可以基于现有类型或现有类型的组合创建新的自定义类型。这包括定义联合类型、交集类型和其他复杂类型。 ```typescript type Point = { x: number; y: number; }; ``` 也可以匿名定义类型: ```typescript const sum = (x: { a: number; b: number }) => x.a + x.b; console.log(sum({ a: 5, b: 1 })); ``` ================================================ FILE: website/src/content/docs/zh-cn/book/optional-properties.md ================================================ --- title: 可选属性 sidebar: order: 12 label: 12. 可选属性 --- 对象可以通过在属性名称末尾添加问号 `?` 来指定可选属性: ```typescript type X = { a: number; b?: number; // 可选的 }; ``` 当属性是可选的时,可以指定默认值 ```typescript type X = { a: number; b?: number; }; const x = ({ a, b = 100 }: X) => a + b; ``` ================================================ FILE: website/src/content/docs/zh-cn/book/others.md ================================================ --- title: 其他 sidebar: order: 61 label: 61. 其他 --- ### 错误和异常处理 TypeScript 允许您使用标准 JavaScript 错误处理机制捕获和处理错误: Try-Catch-Finally 块: ```typescript try { // 可能会抛出异常的代码 } catch (error) { // 处理错误 } finally { // 总是会执行的代码, finally 是可选的 } ``` 您还可以处理不同类型的错误: ```typescript try { // 可能会抛出不同类型错误的代码 } catch (error) { if (error instanceof TypeError) { // 处理 TypeError } else if (error instanceof RangeError) { // 处理 RangeError } else { // 处理其他的错误 } } ``` 自定义错误类型: 可以通过扩展 Error 来指定更具体的错误 `class` : ```typescript class CustomError extends Error { constructor(message: string) { super(message); this.name = 'CustomError'; } } throw new CustomError('This is a custom error.'); ``` ### 混合类 Mixin 类允许您将多个类的行为组合并组合成一个类。它们提供了一种重用和扩展功能的方法,而不需要深层继承链。 ```typescript abstract class Identifiable { name: string = ''; logId() { console.log('id:', this.name); } } abstract class Selectable { selected: boolean = false; select() { this.selected = true; console.log('Select'); } deselect() { this.selected = false; console.log('Deselect'); } } class MyClass { constructor() {} } // 扩展 MyClass 以包含可识别和可选择的行为 interface MyClass extends Identifiable, Selectable {} // 将 mixins 应用于类的函数 function applyMixins(source: any, baseCtors: any[]) { baseCtors.forEach(baseCtor => { Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => { let descriptor = Object.getOwnPropertyDescriptor( baseCtor.prototype, name ); if (descriptor) { Object.defineProperty(source.prototype, name, descriptor); } }); }); } // 将 mixins 应用到 MyClass applyMixins(MyClass, [Identifiable, Selectable]); let o = new MyClass(); o.name = 'abc'; o.logId(); o.select(); ``` ### 异步语言特性 由于 TypeScript 是 JavaScript 的超集,因此它内置了 JavaScript 的异步语言功能,例如: Promises: Promise 是一种处理异步操作及其结果的方法,使用 `.then()`和等方法 `.catch()` 来处理成功和错误条件。 要了解更多信息: [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) Async/await: Async/await 关键字是一种为处理 Promise 提供看起来更同步的语法的方法。`async` 关键字用于定义异步函数,并且 `await` 关键字在异步函数中使用以暂停执行,直到 Promise 被解决或拒绝。 要了解更多信息: [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function) [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await) TypeScript 很好地支持以下 API: Fetch API: [https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) Web Workers: [https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API) Shared Workers: [https://developer.mozilla.org/en-US/docs/Web/API/SharedWorker](https://developer.mozilla.org/en-US/docs/Web/API/SharedWorker) WebSocket: [https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API) ### 迭代器和生成器 TypeScript 对迭代器和生成器都提供了很好的支持。 迭代器是实现迭代器协议的对象,提供了一种逐个访问集合或序列元素的方法。它是一个包含指向迭代中下一个元素的指针的结构。他们有一个 `next()` 方法返回序列中的下一个值以及指示序列是否为 的布尔值 `done` 。 ```typescript class NumberIterator implements Iterable { private current: number; constructor( private start: number, private end: number ) { this.current = start; } public next(): IteratorResult { if (this.current <= this.end) { const value = this.current; this.current++; return { value, done: false }; } else { return { value: undefined, done: true }; } } [Symbol.iterator](): Iterator { return this; } } const iterator = new NumberIterator(1, 3); for (const num of iterator) { console.log(num); } ``` 生成器是使用 `function*` 简化迭代器创建的语法定义的特殊函数。它们使用 `yield` 关键字来定义值的序列,并在请求值时自动暂停和恢复执行。 生成器使创建迭代器变得更加容易,并且对于处理大型或无限序列特别有用。 例子: ```typescript function* numberGenerator(start: number, end: number): Generator { for (let i = start; i <= end; i++) { yield i; } } const generator = numberGenerator(1, 5); for (const num of generator) { console.log(num); } ``` TypeScript 还支持异步迭代器和异步生成器。 要了解更多信息: [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Generator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Generator) [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Iterator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Iterator) ### TsDocs JSDoc 参考 使用 JavaScript 代码库时,可以通过使用 JSDoc 注释和附加注释来提供类型信息,帮助 TypeScript 推断正确的类型。 例子: ```typescript /** * Computes the power of a given number * @constructor * @param {number} base – The base value of the expression * @param {number} exponent – The exponent value of the expression */ function power(base: number, exponent: number) { return Math.pow(base, exponent); } power(10, 2); // function power(base: number, exponent: number): number ``` 此链接提供了完整文档: [https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html](https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html) 从版本 3.7 开始,可以从 JavaScript JSDoc 语法生成 .d.ts 类型定义。更多信息可以在这里找到: [https://www.typescriptlang.org/docs/handbook/declaration-files/dts-from-js.html](https://www.typescriptlang.org/docs/handbook/declaration-files/dts-from-js.html) ### @types @types 组织下的包是特殊的包命名约定,用于为现有 JavaScript 库或模块提供类型定义。例如使用: ```shell npm install --save-dev @types/lodash ``` 将在您当前的项目中安装 `lodash` 的类型定义。 要为 @types 包的类型定义做出贡献,请向 [https://github.com/DefinitelyTyped/DefinitelyTyped](https://github.com/DefinitelyTyped/DefinitelyTyped) 提交pr请求。 ### JSX JSX (JavaScript XML) 是 JavaScript 语言语法的扩展,允许您在 JavaScript 或 TypeScript 文件中编写类似 HTML 的代码。它通常在 React 中用来定义 HTML 结构。 TypeScript 通过提供类型检查和静态分析来扩展 JSX 的功能。 要使用 JSX,您需要在文件 `tsconfig.json` 中设置 `jsx` 编译器选项。两个常见的配置选项: * "preserve": 触发 .jsx 文件且 JSX 不变. 此选项告诉 TypeScript 按原样保留 JSX 语法,而不是在编译过程中对其进行转换。 如果您有单独的工具(例如 Babel)来处理转换,则可以使用此选项。 * "react": 启用 TypeScript 的内置 JSX 转换。 将使用 React.createElement 。 所有选项均可在此处使用: [https://www.typescriptlang.org/tsconfig#jsx](https://www.typescriptlang.org/tsconfig#jsx) ### ES6 模块 TypeScript 确实支持 ES6 (ECMAScript 2015) 和许多后续版本。这意味着您可以使用 ES6 语法,例如箭头函数、模板文字、类、模块、解构等等。 要在项目中启用 ES6 功能,您可以在 `tsconfig.json` 中指定 `target` 属性。 配置示例: ```json { "compilerOptions": { "target": "es6", "module": "es6", "moduleResolution": "node", "sourceMap": true, "outDir": "dist" }, "include": ["src"] } ``` ### ES7 求幂运算符 求幂 (`**`) 运算符计算通过将第一个操作数进行第二个操作数的幂获得的值。它的功能与 `Math.pow()` 类似,但增加了接受 BigInts 作为操作数的功能。TypeScript 完全支持在 `tsconfig.json` 文件中设置 `target` 为 `es2016`或更大版本来使用此运算符。 ```typescript console.log(2 ** (2 ** 2)); // 16 ``` ### for-await-of 语句 这是 TypeScript 完全支持的 JavaScript 功能,它允许您从目标版本 `es2018` 迭代异步可迭代对象。 ```typescript async function* asyncNumbers(): AsyncIterableIterator { yield Promise.resolve(1); yield Promise.resolve(2); yield Promise.resolve(3); } (async () => { for await (const num of asyncNumbers()) { console.log(num); } })(); ``` ### New target 元属性 您可以在 TypeScript 中使用 `new.target` 元属性,该属性使您能够确定是否使用 new 运算符调用函数或构造函数。它允许您检测对象是否是由于构造函数调用而创建的。 ```typescript class Parent { constructor() { console.log(new.target); // 记录用于创建实例的构造函数 } } class Child extends Parent { constructor() { super(); } } const parentX = new Parent(); // [Function: Parent] const child = new Child(); // [Function: Child] ``` ### 动态导入表达式 可以使用 TypeScript 支持的动态导入 ECMAScript 建议有条件地加载模块或按需延迟加载模块。 TypeScript 中动态导入表达式的语法如下: ```typescript async function renderWidget() { const container = document.getElementById('widget'); if (container !== null) { const widget = await import('./widget'); // 动态导入 widget.render(container); } } renderWidget(); ``` ### "tsc –watch" 此命令使用 `--watch` 参数启动 TypeScript 编译器,能够在修改 TypeScript 文件时自动重新编译它们。 ```shell tsc --watch ``` 从 TypeScript 4.9 版本开始,文件监控主要依赖于文件系统事件,如果无法建立基于事件的观察程序,则会自动诉诸轮询。 ### 默认声明 当为变量或参数分配默认值时,将使用默认声明。这意味着如果没有为该变量或参数提供值,则将使用默认值。 ```typescript function greet(name: string = 'Anonymous'): void { console.log(`Hello, ${name}!`); } greet(); // Hello, Anonymous! greet('John'); // Hello, John! ``` ### 可选链 可选的链接运算符 `?.` 与常规点运算符 (`.`) 一样用于访问属性或方法。但是,它通过优雅处理 `undefined` 和 `null` 来终止表达式并返回 `undefined`,而不是抛出错误。 ```typescript type Person = { name: string; age?: number; address?: { street?: string; city?: string; }; }; const person: Person = { name: 'John', }; console.log(person.address?.city); // undefined ``` ### 空合并运算符 如果 `??` 左侧是 `null` 或者 `undefined` ,则空合并运算符返回右侧值,否则,它返回左侧值。 ```typescript const foo = null ?? 'foo'; console.log(foo); // foo const baz = 1 ?? 'baz'; const baz2 = 0 ?? 'baz'; console.log(baz); // 1 console.log(baz2); // 0 ``` ### 模板字符串类型 模板字符串类型允许在类型级别操作字符串值并基于现有字符串生成新的字符串类型。它们对于从基于字符串的操作创建更具表现力和更精确的类型非常有用。 ```typescript type Department = 'enginnering' | 'hr'; type Language = 'english' | 'spanish'; type Id = `${Department}-${Language}-id`; // "enginnering-english-id" | "enginnering-spanish-id" | "hr-english-id" | "hr-spanish-id" ``` ### 函数重载 函数重载允许您为同一函数名定义多个函数签名,每个函数签名具有不同的参数类型和返回类型。当您调用重载函数时,TypeScript 使用提供的参数来确定正确的函数签名: ```typescript function makeGreeting(name: string): string; function makeGreeting(names: string[]): string[]; function makeGreeting(person: unknown): unknown { if (typeof person === 'string') { return `Hi ${person}!`; } else if (Array.isArray(person)) { return person.map(name => `Hi, ${name}!`); } throw new Error('Unable to greet'); } makeGreeting('Simon'); makeGreeting(['Simone', 'John']); ``` ### 递归类型 递归类型是可以引用自身的类型。 这对于定义具有分层或递归结构(可能无限嵌套)的数据结构非常有用,例如链表、树和图。 ```typescript type ListNode = { data: T; next: ListNode | undefined; }; ``` ### 递归条件类型 可以使用 TypeScript 中的逻辑和递归来定义复杂的类型关系。让我们简单地分解一下: 条件类型:允许您基于布尔条件定义类型: ```typescript type CheckNumber = T extends number ? 'Number' : 'Not a number'; type A = CheckNumber<123>; // 'Number' type B = CheckNumber<'abc'>; // 'Not a number' ``` 递归:是指在自己的定义中引用自身的类型定义: ```typescript type Json = string | number | boolean | null | Json[] | { [key: string]: Json }; const data: Json = { prop1: true, prop2: 'prop2', prop3: { prop4: [], }, }; ``` 递归条件类型结合了条件逻辑和递归。这意味着类型定义可以通过条件逻辑依赖于自身,从而创建复杂且灵活的类型关系。 ```typescript type Flatten = T extends Array ? Flatten : T; type NestedArray = [1, [2, [3, 4], 5], 6]; type FlattenedArray = Flatten; // 2 | 3 | 4 | 5 | 1 | 6 ``` ### Node 中的 ECMAScript 模块支持 Node.js 从 15.3.0 版本开始添加了对 ECMAScript 模块的支持,而 TypeScript 从 4.7 版本开始增加了对 Node.js 的 ECMAScript 模块支持。可以通过将 `tsconfig.json` 文件中的`module`属性的值设置为 `nodenext` 来启用此支持。这是一个例子: ```json { "compilerOptions": { "module": "nodenext", "outDir": "./lib", "declaration": true } } ``` Node.js 支持两种模块文件扩展名:`.mjs` 的ES 模块和 `.cjs` 的CommonJS 模块。TypeScript 中的等效文件扩展名适用 `.mts` 于 ES 模块和 `.cts` 于CommonJS 模块。当 TypeScript 编译器将这些文件转译为 JavaScript 时,它将分别创建 `.mjs` 和 `.cjs` 文件。 如果您想在项目中使用 ES 模块,可以type在 package.json 文件中将该属性设置为"module"。这指示 Node.js 将项目视为 ES 模块项目。 此外,TypeScript 还支持 .d.ts 文件中的类型声明。这些声明文件为用 TypeScript 编写的库或模块提供类型信息,允许其他开发人员通过 TypeScript 的类型检查和自动完成功能来利用它们。 ### 断言函数 在 TypeScript 中,断言函数是根据返回值指示特定条件验证的函数。在最简单的形式中,断言函数检查提供的谓词,并在谓词计算结果为 false 时引发错误。 ```typescript function isNumber(value: unknown): asserts value is number { if (typeof value !== 'number') { throw new Error('Not a number'); } } ``` 或者可以声明为函数表达式: ```typescript type AssertIsNumber = (value: unknown) => asserts value is number; const isNumber: AssertIsNumber = value => { if (typeof value !== 'number') { throw new Error('Not a number'); } }; ``` 断言函数与类型保护有相似之处。类型保护最初是为了执行运行时检查并确保值的类型在特定范围内而引入的。具体来说,类型保护是一个计算类型谓词并返回指示谓词是真还是假的布尔值的函数。这与断言函数略有不同,断言函数的目的是在不满足谓词时抛出错误而不是返回 false。 类型保护示例: ```typescript const isNumber = (value: unknown): value is number => typeof value === 'number'; ``` ### 可变参数元组类型 可变元组类型是 TypeScript 4.0 版本中引入的一个功能,让我们通过回顾什么是元组来开始学习它们: 元组类型是一个具有定义长度的数组,并且每个元素的类型已知: ```typescript type Student = [string, number]; const [name, age]: Student = ['Simone', 20]; ``` 术语"可变参数"意味着不定数量(接受可变数量的参数)。 可变参数元组是一种元组类型,它具有以前的所有属性,但确切的形状尚未定义: ```typescript type Bar = [boolean, ...T, number]; type A = Bar<[boolean]>; // [boolean, boolean, number] type B = Bar<['a', 'b']>; // [boolean, 'a', 'b', number] type C = Bar<[]>; // [boolean, number] ``` 在前面的代码中我们可以看到元组形状是由T传入的泛型定义的。 可变参数元组可以接受多个泛型,这使得它们非常灵活: ```typescript type Bar = [...T, boolean, ...G]; type A = Bar<[number], [string]>; // [number, boolean, string] type B = Bar<['a', 'b'], [boolean]>; // ["a", "b", boolean, boolean] ``` 使用新的可变参数元组,我们可以使用: * 元组类型语法中的扩展现在可以是通用的,因此即使我们不知道我们正在操作的实际类型,我们也可以表示元组和数组上的高阶操作 * 其余元素可以出现在元组中的任何位置。 例子: ```typescript type Items = readonly unknown[]; function concat( arr1: T, arr2: U ): [...T, ...U] { return [...arr1, ...arr2]; } concat([1, 2, 3], ['4', '5', '6']); // [1, 2, 3, "4", "5", "6"] ``` ### 装箱类型 装箱类型是指用于将基本类型表示为对象的包装对象。这些包装器对象提供了原始值无法直接使用的附加功能和方法。 当你访问原始 `string` 上的 `charAt` 或者 `normalize` 方法时,JavaScript 将其包装在 `String` 类型的对象中,调用该方法,然后丢弃该对象 示范: ```typescript const originalNormalize = String.prototype.normalize; String.prototype.normalize = function () { console.log(this, typeof this); return originalNormalize.call(this); }; console.log('\u0041'.normalize()); ``` TypeScript 通过为原语及其相应的对象包装器提供单独的类型来表示这种区别: * string => String * number => Number * boolean => Boolean * symbol => Symbol * bigint => BigInt 通常不需要盒装类型。避免使用装箱类型,而是使用基元类型,例如 `string` 代替 `String`。 ### TypeScript 中的协变和逆变 协变和逆变用于描述在处理类型的继承或赋值时关系如何工作。 协变意味着类型关系保留继承或赋值的方向,因此如果类型 A 是类型 B 的子类型,则类型 A 的数组也被视为类型 B 的数组的子类型。这里需要注意的重要事项是维持子类型关系,这意味着协变接受子类型但不接受超类型。 逆变意味着类型关系颠倒了继承或赋值的方向,因此如果类型 A 是类型 B 的子类型,则类型 B 的数组被视为类型 A 数组的子类型。子类型关系颠倒了,这意味着该逆变接受超类型但不接受子类型。 注意:双变量意味着同时接受超类型和子类型。 示例:假设我们有一个适合所有动物的空间和一个专门适合狗的单独空间。 在协方差中,您可以将所有狗放入动物空间中,因为狗是一种动物。但你不能把所有的动物都放在狗的空间里,因为可能还有其他动物混在一起。 在逆变中,您不能将所有动物放入狗空间中,因为动物空间也可能包含其他动物。然而,你可以把所有的狗都放在动物空间里,因为所有的狗也是动物。 ```typescript // 协变示例 class Animal { name: string; constructor(name: string) { this.name = name; } } class Dog extends Animal { breed: string; constructor(name: string, breed: string) { super(name); this.breed = breed; } } let animals: Animal[] = []; let dogs: Dog[] = []; // 协变允许将子类型(狗)数组分配给超类型(动物)数组 animals = dogs; dogs = animals; // 无效: 'Animal[]' 不能赋值给 'Dog[]' // 逆变示例 type Feed = (animal: T) => void; let feedAnimal: Feed = (animal: Animal) => { console.log(`Animal name: ${animal.name}`); }; let feedDog: Feed = (dog: Dog) => { console.log(`Dog name: ${dog.name}, Breed: ${dog.breed}`); }; // 逆变允许将超类型(动物)回调赋值给子类型(狗)回调 feedDog = feedAnimal; feedAnimal = feedDog; // 无效: Type 'Feed' 不能赋值给 'Feed'. ``` 在 TypeScript 中,数组的类型关系是协变的,而函数参数的类型关系是逆变的。这意味着 TypeScript 同时表现出协变和逆变,具体取决于上下文。 #### 类型参数的可选方差注释 从 TypeScript 4.7.0 开始,我们可以使用out和in关键字来具体说明方差注释。 对于协变,使用out关键字: ```typescript type AnimalCallback = () => T; // 此处 T 是协变的 ``` 对于逆变,使用in关键字: ```typescript type AnimalCallback = (value: T) => void; // 此处 T 是逆变的 ``` ### 模板字符串模式索引签名 模板字符串模式索引签名允许我们使用模板字符串模式定义灵活的索引签名。 此功能使我们能够创建可以使用特定字符串键模式进行索引的对象,从而在访问和操作属性时提供更多控制和特异性。 TypeScript 4.4 版开始允许符号和模板字符串模式的索引签名。 ```typescript const uniqueSymbol = Symbol('description'); type MyKeys = `key-${string}`; type MyObject = { [uniqueSymbol]: string; [key: MyKeys]: number; }; const obj: MyObject = { [uniqueSymbol]: 'Unique symbol key', 'key-a': 123, 'key-b': 456, }; console.log(obj[uniqueSymbol]); // Unique symbol key console.log(obj['key-a']); // 123 console.log(obj['key-b']); // 456 ``` ### satisfies操作符 `satisfies` 允许您检查给定类型是否满足特定接口或条件。换句话说,它确保类型具有特定接口所需的所有属性和方法。这是确保变量适合类型定义的一种方法。 下面是一个示例: ```typescript type Columns = 'name' | 'nickName' | 'attributes'; type User = Record; // `User`的类型注释 const user: User = { name: 'Simone', nickName: undefined, attributes: ['dev', 'admin'], }; // 在以下几行中,TypeScript 将无法正确推断 user.attributes?.map(console.log); // 'string | string[]' 中不存在属性 'map'。'string' 中不存在属性 'map'。 user.nickName; // string | string[] | undefined // 类型断言 `as` const user2 = { name: 'Simon', nickName: undefined, attributes: ['dev', 'admin'], } as User; // 这里也一样的, TypeScript 将无法正确推断 user2.attributes?.map(console.log); //'string | string[]' 中不存在属性 'map'。'string' 中不存在属性 'map'。 user2.nickName; // string | string[] | undefined // 使用"satisfies"运算符我们现在可以正确推断类型 const user3 = { name: 'Simon', nickName: undefined, attributes: ['dev', 'admin'], } satisfies User; user3.attributes?.map(console.log); // TypeScript 推断正确: string[] user3.nickName; // TypeScript 推断正确: undefined ``` ### 仅类型导入和导出 仅类型导入和导出允许您导入或导出类型,而无需导入或导出与这些类型关联的值或函数。 这对于减小捆绑包的大小很有用。 要使用仅类型导入,您可以使用`import type`关键字。 TypeScript 允许在仅类型导入中使用声明和实现文件扩展名(.ts、.mts、.cts 和 .tsx),无论`allowImportingTsExtensions`设置如何。 例如: ```typescript import type { House } from './house.ts'; ``` 以下是支持的形式: ```typescript import type T from './mod'; import type { A, B } from './mod'; import type * as Types from './mod'; export type { T }; export type { T } from './mod'; ``` ### 使用声明和显式资源管理 "using"声明是块范围的、不可变的绑定,类似于"const",用于管理一次性资源。 当使用值初始化时,该值的"Symbol.dispose"方法将被记录,并随后在退出封闭块作用域时执行。 这是基于 ECMAScript 的资源管理功能,该功能对于在对象创建后执行基本的清理任务非常有用,例如关闭连接、删除文件和释放内存。 笔记: * 由于最近在 TypeScript 5.2 版中引入,大多数运行时缺乏本机支持。 您将需要以下功能的填充:`Symbol.dispose`、`Symbol.asyncDispose`、`DisposableStack`、`AsyncDisposableStack`、`SuppressedError`。 * 此外,您需要按如下方式配置 tsconfig.json: ```json { "compilerOptions": { "target": "es2022", "lib": ["es2022", "esnext.disposable", "dom"] } } ```` 例子: ```typescript //@ts-ignore Symbol.dispose ??= Symbol('Symbol.dispose'); // 简单的兼容性填充 const doWork = (): Disposable => { return { [Symbol.dispose]: () => { console.log('disposed'); }, }; }; console.log(1); { using work = doWork(); // 资源被声明 console.log(2); } // 资源被释放 (例如, `work[Symbol.dispose]()` 被执行) console.log(3); ``` 该代码将记录: ```shell 1 2 disposed 3 ``` 符合处置条件的资源必须遵守 `Disposable` 接口: ```typescript // lib.esnext.disposable.d.ts interface Disposable { [Symbol.dispose](): void; } ``` "using"声明在堆栈中记录资源处置操作,确保它们以与声明相反的顺序处置: ```typescript { using j = getA(), y = getB(); using k = getC(); } // 先释放 `C`, 然后 `B`, 然后 `A`. ``` 即使发生后续代码或异常,也保证会释放资源。 这可能会导致处置可能引发异常,并可能抑制另一个异常。 为了保留有关被抑制错误的信息,引入了一个新的本机异常"SuppressedError"。 #### 使用声明等待 "await using"声明处理异步一次性资源。 该值必须具有"Symbol.asyncDispose"方法,该方法将在块末尾等待。 ```typescript async function doWorkAsync() { await using work = doWorkAsync(); // 资源被声明 } // // 资源被释放 (例如, `await work[Symbol.asyncDispose]()` 被执行) ``` 对于异步可处置资源,它必须遵守"Disposable"或"AsyncDisposable"接口: ```typescript // lib.esnext.disposable.d.ts interface AsyncDisposable { [Symbol.asyncDispose](): Promise; } ``` ```typescript //@ts-ignore Symbol.asyncDispose ??= Symbol('Symbol.asyncDispose'); // Simple polify class DatabaseConnection implements AsyncDisposable { // 当对象被异步释放时会被调用的方法 [Symbol.asyncDispose]() { // Close the connection and return a promise return this.close(); } async close() { console.log('Closing the connection...'); await new Promise(resolve => setTimeout(resolve, 1000)); console.log('Connection closed.'); } } async function doWork() { // 创建一个新的连接,并在其超出作用域时进行异步释放 await using connection = new DatabaseConnection(); // 资源被声明 console.log('Doing some work...'); } // 资源被释放 (例如, `await connection[Symbol.asyncDispose]()` 被执行) doWork(); ``` 代码日志: ```shell Doing some work... Closing the connection... Connection closed. ``` 语句中允许使用"using"和"await using"声明:"for"、"for-in"、"for-of"、"for-await-of"、"switch"。 ### 导入属性 TypeScript 5.3 的导入属性(导入标签)告诉运行时如何处理模块(JSON 等)。这通过确保干净的导入来提高安全性,并与内容安全策略 (CSP) 保持一致,以实现更安全的资源加载。TypeScript 确保它们有效,但让运行时处理它们的解释以进行特定的模块处理。 示例: ```typescript import config from './config.json' with { type: 'json' }; ``` 使用动态导入: ```typescript const config = import('./config.json', { with: { type: 'json' } }); ``` ================================================ FILE: website/src/content/docs/zh-cn/book/overloads.md ================================================ --- title: 重载 sidebar: order: 51 label: 51. 重载 --- TypeScript 中的函数重载允许您为单个函数名称定义多个函数签名,从而使您能够定义可以多种方式调用的函数。这是一个例子: ```typescript // 重载 function sayHi(name: string): string; function sayHi(names: string[]): string[]; // 实现 function sayHi(name: unknown): unknown { if (typeof name === 'string') { return `Hi, ${name}!`; } else if (Array.isArray(name)) { return name.map(name => `Hi, ${name}!`); } throw new Error('Invalid value'); } sayHi('xx'); // 有效 sayHi(['aa', 'bb']); // 有效 ``` 这是在 `class` 中使用函数重载的另一个示例: ```typescript class Greeter { message: string; constructor(message: string) { this.message = message; } // 重载 sayHi(name: string): string; sayHi(names: string[]): ReadonlyArray; // 实现 sayHi(name: unknown): unknown { if (typeof name === 'string') { return `${this.message}, ${name}!`; } else if (Array.isArray(name)) { return name.map(name => `${this.message}, ${name}!`); } throw new Error('value is invalid'); } } console.log(new Greeter('Hello').sayHi('Simon')); ``` ================================================ FILE: website/src/content/docs/zh-cn/book/predefined-conditional-types.md ================================================ --- title: 预定义条件类型 sidebar: order: 42 label: 42. 预定义条件类型 --- 在 TypeScript 中,预定义的条件类型是语言提供的内置条件类型。它们旨在根据给定类型的特征执行常见的类型转换。 `Exclude`: 此类型从 Type 中删除可分配给 ExcludedType 的所有类型。 `Extract`: 此类型从 Union 中提取可分配给 Type 的所有类型。 `NonNullable`: 此类型从 Type 中删除 null 和 undefined。 `ReturnType`: 此类型提取函数 Type 的返回类型。 `Parameters`: 该类型提取函数类型的参数类型。 `Required`: 此类型使 Type 中的所有属性成为必需。 `Partial`: 此类型使 Type 中的所有属性都是可选的。 `Readonly`: 此类型使 Type 中的所有属性变为只读。 ================================================ FILE: website/src/content/docs/zh-cn/book/primitive-types.md ================================================ --- title: 原始类型 sidebar: order: 10 label: 10. 原始类型 --- TypeScript 支持 7 种基本类型。原始数据类型是指不是对象并且没有任何与其关联的方法的类型。在 TypeScript 中,所有原始类型都是不可变的,这意味着它们的值一旦分配就无法更改。 ### string 原始 `string` 类型存储文本数据,并且值始终是双引号或单引号的。 ```typescript const x: string = 'x'; const y: string = 'y'; ``` 如果字符串被反引号 (`) 字符包围,则字符串可以跨越多行: ```typescript let sentence: string = `xxx, yyy`; ``` ### boolean TypeScript 中的数据 `boolean` 类型存储二进制值,或者true或false。 ```typescript const isReady: boolean = true; ``` ### number TypeScript 中的数据类型 `number` 用 64 位浮点值表示。类型 `number` 可以表示整数和分数。TypeScript 还支持十六进制、二进制和八进制,例如: ```typescript const decimal: number = 10; const hexadecimal: number = 0xa00d; // 十六进制数以 0x 开始 const binary: number = 0b1010; // 二进制数以 0b 开始 const octal: number = 0o633; // 八进制数以 0o 开始 ``` ### bigInt `bigInt` 表示无法用 `number` 表示的非常大的数值 (253 – 1)。 `bigInt` 可以通过调用内置函数 `BigInt()` 或添加 `n` 到任何整数数字字面量的末尾来创建: ```typescript const x: bigint = BigInt(9007199254740991); const y: bigint = 9007199254740991n; ``` 笔记: * `bigInt` 值不能与 `number` 混合,也不能与内置的 `Math` 一起使用,它们必须强制为相同的类型。 * 仅当目标配置为 ES2020 或更高版本时,“bigInt”值才可用。 ### symbol JavaScript 有一个原始函数 Symbol(),它创建一个全局唯一的引用。 ```typescript let sym = Symbol('x'); // symbol 类型 ``` ### null and undefined `null`和 `undefined` 类型都表示没有值或不存在任何值。 `undefined` 类型意味着该值未分配或初始化,或者指示无意中缺少值。 `null` 类型意味着我们知道该字段没有值,因此值不可用,这表明故意不存在值。 ### Array `array` 是一种可以存储多个相同类型或不同类型的值的数据类型。可以使用以下语法定义它: ```typescript const x: string[] = ['a', 'b']; const y: Array = ['a', 'b']; const j: Array = ['a', 1, 'b', 2]; ``` TypeScript 使用以下语法支持只读数组: ```typescript const x: readonly string[] = ['a', 'b']; // 只读修饰符 const y: ReadonlyArray = ['a', 'b']; const j: ReadonlyArray = ['a', 1, 'b', 2]; j.push('x'); // 有效 ``` TypeScript 支持数组和只读数组: ```typescript const x: [string, number] = ['a', 1]; const y: readonly [string, number] = ['a', 1]; ``` ### any 数据 `any` 类型字面上代表"任何"值,当 TypeScript 无法推断类型或未指定时,它是默认值。 使用 `any` 时,TypeScript编译器会跳过类型检查,因此 `any` 使用时不存在类型安全。通常,当发生错误时不要使用 `any` 静默编译器,而是专注于修复错误,因为使用 `any` 它可能会破坏契约,并且我们会失去 TypeScript 自动完成的好处。 在从 JavaScript 逐步迁移到 TypeScript 的过程中,该 `any` 类型可能很有用,因为它可以让编译器保持沉默。 对于新项目,请使用 TypeScript 配置 `noImplicitAny` ,该配置使 TypeScript 能够在any使用或推断时发出错误。 `any` 通常是错误的来源,它可以掩盖类型的实际问题。尽可能避免使用它。 ================================================ FILE: website/src/content/docs/zh-cn/book/readonly-properties.md ================================================ --- title: 只读属性 sidebar: order: 13 label: 13. 只读属性 --- 是否可以通过使用修饰符来防止对属性进行写入,`readonly` 以确保该属性不能被重写,但不提供任何完全不变性的保证: ```typescript interface Y { readonly a: number; } type X = { readonly a: number; }; type J = Readonly<{ a: number; }>; type K = { readonly [index: number]: string; }; ``` ================================================ FILE: website/src/content/docs/zh-cn/book/strictnullchecks.md ================================================ --- title: 严格空检查 sidebar: order: 18 label: 18. 严格空检查 --- `strictNullChecks` 是一个 TypeScript 编译器选项,强制执行严格的 null 检查。启用此选项后,只有在变量和参数已使用联合类型 `null` | `undefined` 显式声明为该类型时,才可以对其进行赋值`null` 或者 `undefined`。如果变量或参数未显式声明为可为空,TypeScript 将生成错误以防止潜在的运行时错误。 ================================================ FILE: website/src/content/docs/zh-cn/book/symbols.md ================================================ --- title: Symbols sidebar: order: 58 label: 58. Symbols --- 符号是一种原始数据类型,表示不可变值,保证在程序的整个生命周期中全局唯一。 符号可以用作对象属性的键,并提供一种创建不可枚举属性的方法。 ```typescript const key1: symbol = Symbol('key1'); const key2: symbol = Symbol('key2'); const obj = { [key1]: 'value 1', [key2]: 'value 2', }; console.log(obj[key1]); // value 1 console.log(obj[key2]); // value 2 ``` 在 WeakMap 和 WeakSet 中,现在允许符号作为键。 ================================================ FILE: website/src/content/docs/zh-cn/book/table-of-contents.md ================================================ --- title: 目录表 sidebar: order: 4 label: 4. 目录表 --- - 简洁的TypeScript之书 - 翻译 - 下载和网站 - 目录表 - 介绍 - 关于作者 - TypeScript简介 - 什么是TypeScript? - 为什么选择 TypeScript? - TypeScript 和 JavaScript - TypeScript 代码生成 - 现在的现代 JavaScript(降级) - TypeScript 入门 - 安装 - 配置 - TypeScript 的配置文件 - target - lib - strict - module - moduleResolution - esModuleInterop - jsx - skipLibCheck - files - include - exclude - importHelpers - 迁移到 TypeScript 的建议 - 探索类型系统 - TypeScript 的语言服务 - 结构类型 - TypeScript 的基本比较规则 - 类型作为集合 - 赋值类型:类型声明和类型断言 - 类型声明 - 类型断言 - 非空断言 - 环境声明 - 属性检测和多余属性检测 - 弱类型 - 严格的对象字面量检测 (Freshness) - 类型推断 - 更高级的推断 - 类型加宽 - 常量 - 类型参数的 const 修饰符 - 常量断言 - 显式类型注释 - 类型缩小 - 条件 - 抛错或者返回 - 可区分联合 - 用户定义的类型保护 - 原始类型 - string - boolean - number - bigInt - symbol - null and undefined - Array - any - 类型注释 - 可选属性 - 只读属性 - 索引签名 - 扩展类型 - 字面量类型 - 字面量推断 - 严格空检查 - 枚举 - 数字枚举 - 字符串枚举 - 常量枚举 - 反向映射 - 环境枚举 - 计算成员和常量成员 - 缩小范围 - typeof 类型保护 - 真实性缩小 - 相等缩小 - In运算符缩小 - instanceof 缩小 - 赋值 - 控制流分析 - 类型谓词 - 可区分联合 - never 类型 - 详尽性检查 - 对象类型 - 元组类型(匿名) - 命名元组类型(已标记) - 固定长度元组 - 联合类型 - 交集类型 - 类型索引 - 值的类型 - Func 返回值的类型 - 模块的类型 - 映射类型 - 映射类型修饰符 - 条件类型 - 分配条件类型 - infer 条件类型中的类型推断 - 预定义条件类型 - 模板联合类型 - 任意类型 - 未知类型 - 空类型 - Never类型 - 接口及类型 - 通用语法 - 基本类型 - 对象和接口 - 并集和交集类型 - 内置原始数据类型 - 常见的内置JS对象 - 重载 - 合并与扩展 - 类型和接口之间的差异 - Class - 通用语法 - 构造函数 - 私有和受保护的构造函数 - 访问修饰符 - Get 与 Set - 类中的自动访问器 - this - 参数属性 - 抽象类 - 使用泛型 - 装饰器 - 类装饰器 - 属性装饰器 - 方法装饰器 - Getter 和 Setter 装饰器 - 装饰器元数据 - 继承 - 静态成员 - 属性初始化 - 方法重载 - 泛型 - 泛型类型 - 泛型类 - 泛型约束 - 泛型上下文缩小 - 擦除的结构类型 - 命名空间 - Symbols - 三斜杠指令 - 类型操作 - 从类型创建类型 - 索引访问类型 - 工具类型 - Awaited\ - Partial\ - Required\ - Readonly\ - Record\ - Pick\ - Omit\ - Exclude\ - Extract\ - NonNullable\ - Parameters\ - ConstructorParameters\ - ReturnType\ - InstanceType\ - ThisParameterType\ - OmitThisParameter\ - ThisType\ - Uppercase\ - Lowercase\ - Capitalize\ - Uncapitalize\ - NoInfer\ - 其他 - 错误和异常处理 - 混合类 - 异步语言特性 - 迭代器和生成器 - TsDocs JSDoc 参考 - @types - JSX - ES6 模块 - ES7 求幂运算符 - for-await-of 语句 - New target 元属性 - 动态导入表达式 - "tsc –watch" - 默认声明 - 可选链 - 空合并运算符 - 模板字符串类型 - 函数重载 - 递归类型 - 递归条件类型 - Node 中的 ECMAScript 模块支持 - 断言函数 - 可变参数元组类型 - 装箱类型 - TypeScript 中的协变和逆变 - 类型参数的可选方差注释 - 模板字符串模式索引签名 - satisfies操作符 - 仅类型导入和导出 - 使用声明和显式资源管理 - 使用声明等待 - 导入属性 ================================================ FILE: website/src/content/docs/zh-cn/book/template-union-types.md ================================================ --- title: 模板联合类型 sidebar: order: 43 label: 43. 模板联合类型 --- 模板联合类型可用于合并和操作类型系统内的文本,例如: ```typescript type Status = 'active' | 'inactive'; type Products = 'p1' | 'p2'; type ProductId = `id-${Products}-${Status}`; // "id-p1-active" | "id-p1-inactive" | "id-p2-active" | "id-p2-inactive" ``` ================================================ FILE: website/src/content/docs/zh-cn/book/the-concise-typescript-book.md ================================================ --- title: 简洁的TypeScript之书 sidebar: order: 1 label: 1. 简洁的TypeScript之书 --- 《Concise TypeScript Book》全面而简洁地概述了 TypeScript 的功能。它提供了清晰的解释,涵盖了该语言最新版本中的所有方面,从强大的类型系统到高级功能。无论您是初学者还是经验丰富的开发人员,本书都是增强您对 TypeScript 的理解和熟练程度的宝贵资源。 本书完全免费且开源。 我相信高质量的技术教育应该惠及所有人,因此我坚持本书免费开源。 如果本书帮助您解决了 bug、理解了棘手的概念或提升了您的职业发展,请考虑以您认为合适的金额(建议价格:15 美元)或请我喝杯咖啡来支持我的工作。您的支持将帮助我保持内容更新,并添加新的示例和更深入的解释。 [![Buy Me a Coffee](https://img.shields.io/badge/buy_me_a_coffee-FFDD00?style=for-the-badge&logo=buy-me-a-coffee&logoColor=black)](https://www.buymeacoffee.com/simonepoggiali) [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/donate/?business=QW82ZS956XLFY&no_recurring=0¤cy_code=EUR) ================================================ FILE: website/src/content/docs/zh-cn/book/the-never-type.md ================================================ --- title: never 类型 sidebar: order: 25 label: 25. never 类型 --- 当变量缩小为不能包含任何值的类型时,TypeScript 编译器将推断该变量必须属于该never类型。这是因为 never 类型代表永远无法生成的值。 ```typescript const printValue = (val: string | number) => { if (typeof val === 'string') { console.log(val.toUpperCase()); } else if (typeof val === 'number') { console.log(val.toFixed(2)); } else { // val 在这里的类型为 never,因为它只能是字符串或数字 const neverVal: never = val; console.log(`Unexpected value: ${neverVal}`); } }; ``` ================================================ FILE: website/src/content/docs/zh-cn/book/translations.md ================================================ --- title: 翻译 sidebar: order: 2 label: 2. 翻译 --- 本书已被翻译成多种语言版本,包括: [中文](https://github.com/gibbok/typescript-book/blob/main/README-zh_CN.md) [意大利](https://github.com/gibbok/typescript-book/blob/main/README-it_IT.md) [葡萄牙语(巴西)](https://github.com/gibbok/typescript-book/blob/main/README-pt_BR.md) [瑞典语](https://github.com/gibbok/typescript-book/blob/main/README-sv_SE.md) ================================================ FILE: website/src/content/docs/zh-cn/book/triple-slash-directives.md ================================================ --- title: 三斜杠指令 sidebar: order: 59 label: 59. 三斜杠指令 --- 三斜杠指令是特殊注释,为编译器提供有关如何处理文件的说明。这些指令以三个连续斜杠 (`///`) 开头,通常放置在 TypeScript 文件的顶部,对运行时行为没有影响。 三斜杠指令用于引用外部依赖项、指定模块加载行为、启用/禁用某些编译器功能等等。几个例子: 引用声明文件: ```typescript /// ``` 指明模块格式: ```typescript /// ``` 启用编译器选项,在以下示例中严格模式: ```typescript /// ``` ================================================ FILE: website/src/content/docs/zh-cn/book/tuple-type-anonymous.md ================================================ --- title: 元组类型(匿名) sidebar: order: 28 label: 28. 元组类型(匿名) --- 元组类型是一种表示具有固定数量的元素及其相应类型的数组的类型。元组类型以固定顺序强制执行特定数量的元素及其各自的类型。当您想要表示具有特定类型的值的集合时,元组类型非常有用,其中数组中每个元素的位置都有特定的含义。 ```typescript type Point = [number, number]; ``` ================================================ FILE: website/src/content/docs/zh-cn/book/type-annotations.md ================================================ --- title: 类型注释 sidebar: order: 11 label: 11. 类型注释 --- 在使用 `var` 、 `let` 和 `const` 声明变量时,可以选择添加类型: ```typescript const x: number = 1; ``` TypeScript 在推断类型方面做得很好,尤其是简单类型时,因此在大多数情况下这些声明是不必要的。 在函数上可以向参数添加类型注释: ```typescript function sum(a: number, b: number) { return a + b; } ``` 以下是使用匿名函数(所谓的 lambda 函数)的示例: ```typescript const sum = (a: number, b: number) => a + b; ``` 当参数存在默认值时可以避免这些注释: ```typescript const sum = (a = 10, b: number) => a + b; ``` 可以将返回类型注释添加到函数中: ```typescript const sum = (a = 10, b: number): number => a + b; ``` 这对于更复杂的函数尤其有用,因为在实现之前编写显式返回类型可以帮助更好地思考该函数。 通常考虑注释类型签名,但不注释主体局部变量,并始终将类型添加到对象字面量中。 ================================================ FILE: website/src/content/docs/zh-cn/book/type-from-func-return.md ================================================ --- title: Func 返回值的类型 sidebar: order: 35 label: 35. Func 返回值的类型 --- Func Return 中的类型是指根据函数的实现自动推断函数的返回类型的能力。这允许 TypeScript 无需显式类型注释即可确定函数返回值的类型。 ```typescript const add = (x: number, y: number) => x + y; // TypeScript 可以推断函数的返回类型是数字 ``` ================================================ FILE: website/src/content/docs/zh-cn/book/type-from-module.md ================================================ --- title: 模块的类型 sidebar: order: 36 label: 36. 模块的类型 --- 模块的类型是指使用模块的导出值自动推断其类型的能力。当模块导出特定类型的值时,TypeScript 可以使用该信息在将该值导入到另一个模块时自动推断该值的类型。 ```typescript export const add = (x: number, y: number) => x + y; // index.ts import { add } from 'calc'; const r = add(1, 2); // r 是 number 类型 ``` ================================================ FILE: website/src/content/docs/zh-cn/book/type-from-value.md ================================================ --- title: 值的类型 sidebar: order: 34 label: 34. 值的类型 --- TypeScript 中的"Type from Value"是指通过类型推断从值或表达式自动推断出类型。 ```typescript const x = 'x'; // TypeScript 可以自动推断变量的类型是 string ``` ================================================ FILE: website/src/content/docs/zh-cn/book/type-indexing.md ================================================ --- title: 类型索引 sidebar: order: 33 label: 33. 类型索引 --- 类型索引是指能够通过预先未知的键来定义可以索引的类型,使用索引签名来指定未显式声明的属性的类型。 ```typescript type Dictionary = { [key: string]: T; }; const myDict: Dictionary = { a: 'a', b: 'b' }; console.log(myDict['a']); // 返回 a ``` ================================================ FILE: website/src/content/docs/zh-cn/book/type-manipulation.md ================================================ --- title: 类型操作 sidebar: order: 60 label: 60. 类型操作 --- ### 从类型创建类型 是否可以通过组合、操作或转换现有类型来创建新类型。 交集类型 ( &): 允许您将多种类型组合成单一类型: ```typescript type A = { foo: number }; type B = { bar: string }; type C = A & B; // A和B的交集 const obj: C = { foo: 42, bar: 'hello' }; ``` 联合类型 (`|`): 允许您定义可以是以下几种类型之一的类型 ```typescript type Result = string | number; const value1: Result = 'hello'; const value2: Result = 42; ``` 映射类型: 允许您转换现有类型的属性以创建新类型: ```typescript type Mutable = { readonly [P in keyof T]: T[P]; }; type Person = { name: string; age: number; }; type ImmutablePerson = Mutable; // 属性变为只读 ``` 条件类型: 允许您根据某些条件创建类型: ```typescript type ExtractParam = T extends (param: infer P) => any ? P : never; type MyFunction = (name: string) => number; type ParamType = ExtractParam; // string ``` ### 索引访问类型 在 TypeScript 中,可以使用索引访问和操作另一个类型中的属性类型 `Type[Key]`。 ```typescript type Person = { name: string; age: number; }; type AgeType = Person['age']; // number ``` ```typescript type MyTuple = [string, number, boolean]; type MyType = MyTuple[2]; // boolean ``` ### 工具类型 可以使用几种内置工具来操作类型,下面列出了最常用的: #### Awaited\ 构造一个递归解包 Promise 类型的类型。 ```typescript type A = Awaited>; // string ``` #### Partial\ 构造一个类型,并将 T 的所有属性设置为可选。 ```typescript type Person = { name: string; age: number; }; type A = Partial; // { name?: string | undefined; age?: number | undefined; } ``` #### Required\ 构造一个类型,并将 T 的所有属性设置为必需。 ```typescript type Person = { name?: string; age?: number; }; type A = Required; // { name: string; age: number; } ``` #### Readonly\ 构造一个类型,并将 T 的所有属性设置为只读。 ```typescript type Person = { name: string; age: number; }; type A = Readonly; const a: A = { name: 'Simon', age: 17 }; a.name = 'John'; // 无效 ``` #### Record\ 构造一个具有类型 T 的一组属性 K 的类型。 ```typescript type Product = { name: string; price: number; }; const products: Record = { apple: { name: 'Apple', price: 0.5 }, banana: { name: 'Banana', price: 0.25 }, }; console.log(products.apple); // { name: 'Apple', price: 0.5 } ``` #### Pick\ 通过从 T 中选取指定属性 K 来构造类型。 ```typescript type Product = { name: string; price: number; }; type Price = Pick; // { price: number; } ``` #### Omit\ 通过从 T 中省略指定属性 K 来构造类型。 ```typescript type Product = { name: string; price: number; }; type Name = Omit; // { name: string; } ``` #### Exclude\ 通过从 T 中排除类型 U 的所有值来构造类型。 ```typescript type Union = 'a' | 'b' | 'c'; type MyType = Exclude; // b ``` #### Extract\ 通过从 T 中提取类型 U 的所有值来构造类型。 ```typescript type Union = 'a' | 'b' | 'c'; type MyType = Extract; // a | c ``` #### NonNullable\ 通过从 T 中排除 null 和 undefined 来构造类型。 ```typescript type Union = 'a' | null | undefined | 'b'; type MyType = NonNullable; // 'a' | 'b' ``` #### Parameters\ 提取函数类型 T 的参数类型。 ```typescript type Func = (a: string, b: number) => void; type MyType = Parameters; // [a: string, b: number] ``` #### ConstructorParameters\ 提取构造函数类型 T 的参数类型。 ```typescript class Person { constructor( public name: string, public age: number ) {} } type PersonConstructorParams = ConstructorParameters; // [name: string, age: number] const params: PersonConstructorParams = ['John', 30]; const person = new Person(...params); console.log(person); // Person { name: 'John', age: 30 } ``` #### ReturnType\ 提取函数类型 T 的返回类型。 ```typescript type Func = (name: string) => number; type MyType = ReturnType; // number ``` #### InstanceType\ 提取类类型 T 的实例类型。 ```typescript class Person { name: string; constructor(name: string) { this.name = name; } sayHello() { console.log(`Hello, my name is ${this.name}!`); } } type PersonInstance = InstanceType; const person: PersonInstance = new Person('John'); person.sayHello(); // Hello, my name is John! ``` #### ThisParameterType\ 从函数类型 T 中提取"this"参数的类型。 ```typescript interface Person { name: string; greet(this: Person): void; } type PersonThisType = ThisParameterType; // Person ``` #### OmitThisParameter\ 从函数类型 T 中删除"this"参数。 ```typescript function capitalize(this: String) { return this[0].toUpperCase + this.substring(1).toLowerCase(); } type CapitalizeType = OmitThisParameter; // () => string ``` #### ThisType\ 作为上下文类型 `this` 的一部分。 ```typescript type Logger = { log: (error: string) => void; }; let helperFunctions: { [name: string]: Function } & ThisType = { hello: function () { this.log('some error'); // 有效,因为"log"是"this"的一部分 this.update(); // 无效 }, }; ``` #### Uppercase\ 将输入类型 T 的名称设为大写。 ```typescript type MyType = Uppercase<'abc'>; // "ABC" ``` #### Lowercase\ 将输入类型 T 的名称设为小写。 ```typescript type MyType = Lowercase<'ABC'>; // "abc" ``` #### Capitalize\ 输入类型 T 的名称大写。 ```typescript type MyType = Capitalize<'abc'>; // "Abc" ``` #### Uncapitalize\ 将输入类型 T 的名称取消大写。 ```typescript type MyType = Uncapitalize<'Abc'>; // "abc" ``` #### NoInfer\ NoInfer 是一种实用类型,旨在阻止泛型函数范围内类型的自动推断。 示例: ```typescript // 泛型函数范围内类型的自动推断。 function fn(x: T[], y: T) { return x.concat(y); } const r = fn(['a', 'b'], 'c'); // 此处的类型为 ("a" | "b" | "c")[] ``` 使用 NoInfer: ```typescript // 使用 NoInfer 阻止类型推断的示例函数 function fn2(x: T[], y: NoInfer) { return x.concat(y); } const r2 = fn2(['a', 'b'], 'c'); // 错误:类型为“c”的类型参数不能分配给类型为“a”|“b”的参数。 ``` ================================================ FILE: website/src/content/docs/zh-cn/book/type-predicates.md ================================================ --- title: 类型谓词 sidebar: order: 23 label: 23. 类型谓词 --- TypeScript 中的类型谓词是返回布尔值的函数,用于将变量的类型缩小为更具体的类型。 ```typescript const isString = (value: unknown): value is string => typeof value === 'string'; const foo = (bar: unknown) => { if (isString(bar)) { console.log(bar.toUpperCase()); } else { console.log('not a string'); } }; ``` ================================================ FILE: website/src/content/docs/zh-cn/book/typescript-introduction.md ================================================ --- title: TypeScript简介 sidebar: order: 7 label: 7. TypeScript简介 --- ### 什么是TypeScript? TypeScript 是一种基于 JavaScript 的强类型编程语言。它最初由 Anders Hejlsberg 于 2012 年设计,目前由微软开发和维护,是一个开源项目。 TypeScript 会被编译成 JavaScript,并可在任何 JavaScript 运行时环境(例如浏览器或服务器上的 Node.js)中执行。 它支持多种编程范式,例如函数式编程、泛型编程、命令式编程和面向对象编程。TypeScript 是一种编译型(转译型)语言,在执行前会被转换为 JavaScript。 ### 为什么选择 TypeScript? TypeScript 是一种强类型语言,有助于防止常见的编程错误,并在程序执行之前避免某些类型的运行时错误。 强类型语言允许开发人员在数据类型定义中指定各种程序约束和行为,从而有助于验证软件的正确性并防止缺陷。这在大规模应用中尤其有价值。 TypeScript 的一些好处 * 静态类型,可选强类型 * 类型推断 * 能使用ES6和ES7的新功能 * 跨平台和跨浏览器兼容性 \* IntelliSense 工具支持 ### TypeScript 和 JavaScript TypeScript是用`.ts`或`.tsx`文件编写的, 而JavaScript是用`.js`或`.jsx`文件编写的。 扩展名为.tsx或.jsx的文件可以包含 JavaScript 语法扩展 JSX,该扩展在 React 中用于 UI 开发。 就语法而言,TypeScript 是 JavaScript (ECMAScript 2015) 的类型化超集。所有 JavaScript 代码都是有效的 TypeScript 代码,但反之则不然。 例如,考虑 JavaScript 文件中具有.js扩展名的函数,如下所示: ```typescript const sum = (a, b) => a + b; ``` 该函数可以通过将文件扩展名更改为 .TypeScript 来转换和使用.ts。但是,如果同一个函数使用 TypeScript 类型进行注释,则未经编译就无法在任何 JavaScript 运行时中执行。如果未编译以下 TypeScript 代码,将会产生语法错误 ```typescript const sum = (a: number, b: number): number => a + b; ``` TypeScript 旨在通过让开发人员使用类型注释定义意图来检测编译期间运行时可能发生的异常。此外,如果没有提供类型注释,TypeScript 也可以捕获问题。例如,以下代码片段未指定任何 TypeScript 类型: ```typescript const items = [{ x: 1 }, { x: 2 }]; const result = items.filter(item => item.y); ``` 在这种情况下,TypeScript 检测到错误并报告: ```text 类型 '{ x: number; }' 上不存在属性 'y' 。 ``` TypeScript 的类型系统很大程度上受到 JavaScript 运行时行为的影响。例如,加法运算符 (+) 在 JavaScript 中可以执行字符串连接或数字加法,在 TypeScript 中以相同的方式建模: ```typescript const result = '1' + 1; // 结果是string类型 ``` TypeScript 背后的团队经过深思熟虑,决定将 JavaScript 的异常使用标记为错误。例如,考虑以下有效的 JavaScript 代码: ```typescript const result = 1 + true; // 在JavaScript中, 结果等于2 ``` 但是,TypeScript 会抛出错误: ```text 运算符"+"不能应用于类型"number"和"boolean"。 `````` 出现此错误的原因是 TypeScript 严格强制执行类型兼容性,在这种情况下,它标识了数字和布尔值之间的无效操作。 ### TypeScript 代码生成 TypeScript 编译器有两个主要职责:检查类型错误和编译为 JavaScript。这两个过程是相互独立的。类型不会影响 JavaScript 运行时中代码的执行,因为它们在编译过程中会被完全擦除。即使存在类型错误,TypeScript 仍然可以输出 JavaScript。以下是存在类型错误的 TypeScript 代码示例: ```typescript const add = (a: number, b: number): number => a + b; const result = add('x', 'y'); // "字符串"类型的参数不可赋值给"数字"类型的参数 ``` 但是,它仍然可以生成可执行的 JavaScript 输出: ```typescript 'use strict'; const add = (a, b) => a + b; const result = add('x', 'y'); // xy ``` 无法在运行时检查 TypeScript 类型。例如: ```typescript interface Animal { name: string; } interface Dog extends Animal { bark: () => void; } interface Cat extends Animal { meow: () => void; } const makeNoise = (animal: Animal) => { if (animal instanceof Dog) { // "Dog"仅指一种类型,但在这里用作值。 // ... } }; ``` 由于编译后类型被删除,因此无法在 JavaScript 中运行此代码。为了在运行时识别类型,我们需要使用另一种机制。TypeScript 提供了多种选项,其中常见的一个是 "标签联合(tagged union)"。例如: ```typescript interface Dog { kind: 'dog'; // 标签联合 bark: () => void; } interface Cat { kind: 'cat'; // 标签联合 meow: () => void; } type Animal = Dog | Cat; const makeNoise = (animal: Animal) => { if (animal.kind === 'dog') { animal.bark(); } else { animal.meow(); } }; const dog: Dog = { kind: 'dog', bark: () => console.log('bark'), }; makeNoise(dog); ``` 属性"kind"是一个可以在运行时用来区分 JavaScript 中的对象的值。 运行时的值也可能具有与类型声明中声明的类型不同的类型。例如,如果开发人员误解了 API 类型并对其进行了错误注释。 TypeScript 是 JavaScript 的超集,因此"class"关键字可以在运行时用作类型和值。 ```typescript class Animal { constructor(public name: string) {} } class Dog extends Animal { constructor( public name: string, public bark: () => void ) { super(name); } } class Cat extends Animal { constructor( public name: string, public meow: () => void ) { super(name); } } type Mammal = Dog | Cat; const makeNoise = (mammal: Mammal) => { if (mammal instanceof Dog) { mammal.bark(); } else { mammal.meow(); } }; const dog = new Dog('Fido', () => console.log('bark')); makeNoise(dog); ``` 在 JavaScript 中,"类"具有"prototype"属性,"instanceof"运算符可用于测试构造函数的原型属性是否出现在对象原型链中的任何位置。 TypeScript 对运行时性能没有影响,因为所有类型都将被删除。然而,TypeScript 确实引入了一些构建时间开销。 ### 现在的现代 JavaScript(降级) TypeScript 可以将代码编译为自 ECMAScript 3 (1999) 以来任何已发布的 JavaScript 版本。这意味着 TypeScript 可以将代码从最新的 JavaScript 功能转换为旧版本,这一过程称为降级。这允许使用现代 JavaScript,同时保持与旧运行时环境的最大兼容性。 值得注意的是,在转换为旧版本 JavaScript 的过程中,TypeScript 可能会生成与本机实现相比会产生性能开销的代码。 以下是一些可以在 TypeScript 中使用的现代 JavaScript 功能: * ECMAScript 模块,而不是 AMD 风格的"define"回调或 CommonJS 的"require"语句。 * 用类代替原型。 * 变量声明使用"let"或"const"而不是"var"。 * "for-of"循环或".forEach"而不是传统的"for"循环。 * 用箭头函数代替函数表达式。 * 解构赋值。 * 简写属性/方法名称和计算属性名称。 * 默认函数参数。 通过利用这些现代 JavaScript 功能,开发人员可以在 TypeScript 中编写更具表现力和简洁的代码。 ================================================ FILE: website/src/content/docs/zh-cn/book/union-type.md ================================================ --- title: 联合类型 sidebar: order: 31 label: 31. 联合类型 --- 联合类型是一种表示值的类型,该值可以是多种类型之一。联合类型使用 `|` 表示 每种可能类型之间的符号。 ```typescript let x: string | number; x = 'hello'; // 有效 x = 123; // 有效 ``` ================================================ FILE: website/src/content/docs/zh-cn/book/unknown-type.md ================================================ --- title: 未知类型 sidebar: order: 45 label: 45. 未知类型 --- 在 TypeScript 中,未知类型表示未知类型的值。与允许任何类型值的 `any` 类型不同,`unknown` 需要在以特定方式使用它之前进行类型检查或断言,因此在未首先断言或缩小到更具体的类型的情况下,不允许对 `unknown` 进行任何操作 。 `unknown` 类型只能分配给任何类型和未知类型本身,它是any 的类型安全替代方案。 ```typescript let value: unknown; let value1: unknown = value; // 有效 let value2: any = value; // 有效 let value3: boolean = value; // 无效 let value4: number = value; // 无效 ``` ```typescript const add = (a: unknown, b: unknown): number | undefined => typeof a === 'number' && typeof b === 'number' ? a + b : undefined; console.log(add(1, 2)); // 3 console.log(add('x', 2)); // undefined ``` ================================================ FILE: website/src/content/docs/zh-cn/book/void-type.md ================================================ --- title: 空类型 sidebar: order: 46 label: 46. 空类型 --- `void` 类型用于指示函数不返回值。 ```typescript const sayHello = (): void => { console.log('Hello!'); }; ``` ================================================ FILE: website/src/content/docs/zh-cn/index.mdx ================================================ --- title: TypeScript Book description: The Concise TypeScript Book template: splash hero: tagline: 《Concise TypeScript Book》全面而简洁地概述了 TypeScript 的功能。它提供了清晰的解释,涵盖了该语言最新版本中的所有方面,从强大的类型系统到高级功能。无论您是初学者还是经验丰富的开发人员,本书都是增强您对 TypeScript 的理解和熟练程度的宝贵资源。

本书完全免费且开源。 actions: - text: 现在读! link: /typescript-book/zh-cn/book/the-concise-typescript-book/ icon: right-arrow variant: primary - text: GitHub link: https://github.com/gibbok/typescript-book icon: github variant: secondary - text: X.com link: https://twitter.com/gibbok_coding icon: x.com variant: secondary --- ================================================ FILE: website/src/content/i18n/zh-cn.json ================================================ {} ================================================ FILE: website/src/env.d.ts ================================================ /// /// ================================================ FILE: website/src/styles/custom.css ================================================ /* Dark mode colors. */ :root { --sl-color-accent-low: #36113e; --sl-color-accent: #a400c0; --sl-color-accent-high: #e3b6ed; --sl-color-white: #ffffff; --sl-color-gray-1: #f2e9fd; --sl-color-gray-2: #c7bdd5; --sl-color-gray-3: #9581ae; --sl-color-gray-4: #614e78; --sl-color-gray-5: #412e55; --sl-color-gray-6: #2f1c42; --sl-color-black: #1c1425; } /* Light mode colors. */ :root[data-theme='light'] { --sl-color-accent-low: #ebc9f3; --sl-color-accent: #a700c3; --sl-color-accent-high: #4e0e5b; --sl-color-white: #1c1425; --sl-color-gray-1: #2f1c42; --sl-color-gray-2: #412e55; --sl-color-gray-3: #614e78; --sl-color-gray-4: #9581ae; --sl-color-gray-5: #c7bdd5; --sl-color-gray-6: #f2e9fd; --sl-color-gray-7: #f8f4fe; --sl-color-black: #ffffff; } @media (min-width: 72rem) { .right-sidebar-container { min-width: 27.188rem; /* Carbon ads layout adjustments for screens ≤ 1366px width */ } } .right-sidebar-panel { padding: 0.5rem var(--sl-sidebar-pad-x); } ================================================ FILE: website/tsconfig.json ================================================ { "extends": "astro/tsconfigs/strict" }

We use cookies on our site to enhance your user experience, provide personalized content, and analyze our traffic. Cookie Policy.