├── .editorconfig ├── .gitattributes ├── .github ├── ISSUE_TEMPLATE │ ├── 1 new type.yml │ ├── 2 bug report.yml │ └── 3 enhancement.md ├── contributing.md ├── dependabot.yml ├── funding.yml ├── pull_request_template.md ├── security.md └── workflows │ ├── main.yml │ └── ts-canary.yml ├── .gitignore ├── .npmrc ├── index.d.ts ├── license-cc0 ├── license-mit ├── lint-rules ├── import-path.js └── source-files-extension.js ├── media ├── logo.png ├── logo.sketch ├── logo.svg ├── logo@2x.png └── readme.md ├── package.json ├── readme.md ├── source ├── all-extend.d.ts ├── all-union-fields.d.ts ├── and.d.ts ├── array-indices.d.ts ├── array-slice.d.ts ├── array-splice.d.ts ├── array-tail.d.ts ├── array-values.d.ts ├── arrayable.d.ts ├── async-return-type.d.ts ├── asyncify.d.ts ├── basic.d.ts ├── camel-case.d.ts ├── camel-cased-properties-deep.d.ts ├── camel-cased-properties.d.ts ├── characters.d.ts ├── conditional-except.d.ts ├── conditional-keys.d.ts ├── conditional-pick-deep.d.ts ├── conditional-pick.d.ts ├── conditional-simplify-deep.d.ts ├── conditional-simplify.d.ts ├── delimiter-case.d.ts ├── delimiter-cased-properties-deep.d.ts ├── delimiter-cased-properties.d.ts ├── distributed-omit.d.ts ├── distributed-pick.d.ts ├── empty-object.d.ts ├── enforce-optional.d.ts ├── entries.d.ts ├── entry.d.ts ├── exact.d.ts ├── except.d.ts ├── exclude-strict.d.ts ├── extends-strict.d.ts ├── extract-strict.d.ts ├── find-global-type.d.ts ├── fixed-length-array.d.ts ├── get.d.ts ├── global-this.d.ts ├── globals │ ├── index.d.ts │ └── observable-like.d.ts ├── greater-than-or-equal.d.ts ├── greater-than.d.ts ├── has-optional-keys.d.ts ├── has-readonly-keys.d.ts ├── has-required-keys.d.ts ├── has-writable-keys.d.ts ├── if-any.d.ts ├── if-empty-object.d.ts ├── if-never.d.ts ├── if-null.d.ts ├── if-unknown.d.ts ├── if.d.ts ├── includes.d.ts ├── int-closed-range.d.ts ├── int-range.d.ts ├── internal │ ├── array.d.ts │ ├── characters.d.ts │ ├── index.d.ts │ ├── keys.d.ts │ ├── numeric.d.ts │ ├── object.d.ts │ ├── string.d.ts │ ├── tuple.d.ts │ └── type.d.ts ├── invariant-of.d.ts ├── is-any.d.ts ├── is-equal.d.ts ├── is-float.d.ts ├── is-integer.d.ts ├── is-literal.d.ts ├── is-lowercase.d.ts ├── is-never.d.ts ├── is-null.d.ts ├── is-nullable.d.ts ├── is-optional.d.ts ├── is-tuple.d.ts ├── is-union.d.ts ├── is-unknown.d.ts ├── is-uppercase.d.ts ├── iterable-element.d.ts ├── join.d.ts ├── json-value.d.ts ├── jsonifiable.d.ts ├── jsonify.d.ts ├── kebab-case.d.ts ├── kebab-cased-properties-deep.d.ts ├── kebab-cased-properties.d.ts ├── key-as-string.d.ts ├── keys-of-union.d.ts ├── last-array-element.d.ts ├── less-than-or-equal.d.ts ├── less-than.d.ts ├── literal-to-primitive-deep.d.ts ├── literal-to-primitive.d.ts ├── literal-union.d.ts ├── merge-deep.d.ts ├── merge-exclusive.d.ts ├── merge.d.ts ├── multidimensional-array.d.ts ├── multidimensional-readonly-array.d.ts ├── non-empty-object.d.ts ├── non-empty-string.d.ts ├── non-empty-tuple.d.ts ├── numeric.d.ts ├── omit-deep.d.ts ├── omit-index-signature.d.ts ├── opaque.d.ts ├── optional-keys-of.d.ts ├── or.d.ts ├── override-properties.d.ts ├── package-json.d.ts ├── partial-deep.d.ts ├── partial-on-undefined-deep.d.ts ├── pascal-case.d.ts ├── pascal-cased-properties-deep.d.ts ├── pascal-cased-properties.d.ts ├── paths.d.ts ├── pick-deep.d.ts ├── pick-index-signature.d.ts ├── primitive.d.ts ├── promisable.d.ts ├── readonly-deep.d.ts ├── readonly-keys-of.d.ts ├── readonly-tuple.d.ts ├── remove-prefix.d.ts ├── replace.d.ts ├── require-all-or-none.d.ts ├── require-at-least-one.d.ts ├── require-exactly-one.d.ts ├── require-one-or-none.d.ts ├── required-deep.d.ts ├── required-keys-of.d.ts ├── schema.d.ts ├── screaming-snake-case.d.ts ├── set-field-type.d.ts ├── set-non-nullable-deep.d.ts ├── set-non-nullable.d.ts ├── set-optional.d.ts ├── set-parameter-type.d.ts ├── set-readonly.d.ts ├── set-required-deep.d.ts ├── set-required.d.ts ├── set-return-type.d.ts ├── shared-union-fields-deep.d.ts ├── shared-union-fields.d.ts ├── simplify-deep.d.ts ├── simplify.d.ts ├── single-key-object.d.ts ├── snake-case.d.ts ├── snake-cased-properties-deep.d.ts ├── snake-cased-properties.d.ts ├── split.d.ts ├── spread.d.ts ├── string-repeat.d.ts ├── string-slice.d.ts ├── stringified.d.ts ├── structured-cloneable.d.ts ├── subtract.d.ts ├── sum.d.ts ├── tagged-union.d.ts ├── tagged.d.ts ├── trim.d.ts ├── tsconfig-json.d.ts ├── tuple-to-object.d.ts ├── tuple-to-union.d.ts ├── typed-array.d.ts ├── undefined-on-partial-deep.d.ts ├── union-to-intersection.d.ts ├── union-to-tuple.d.ts ├── unknown-array.d.ts ├── unknown-map.d.ts ├── unknown-record.d.ts ├── unknown-set.d.ts ├── value-of.d.ts ├── words.d.ts ├── writable-deep.d.ts ├── writable-keys-of.d.ts └── writable.d.ts ├── test-d ├── abstract-class.ts ├── all-extend.ts ├── all-union-fields.ts ├── and.ts ├── array-indices.ts ├── array-slice.ts ├── array-splice.ts ├── array-tail.ts ├── array-values.ts ├── arrayable.ts ├── async-return-type.ts ├── asyncify.ts ├── camel-case.ts ├── camel-cased-properties-deep.ts ├── camel-cased-properties.ts ├── class.ts ├── conditional-except.ts ├── conditional-keys.ts ├── conditional-pick-deep.ts ├── conditional-pick.ts ├── conditional-simplify-deep.ts ├── conditional-simplify.ts ├── delimiter-case.ts ├── delimiter-cased-properties-deep.ts ├── delimiter-cased-properties.ts ├── distributed-omit.ts ├── distributed-pick.ts ├── empty-object.ts ├── enforce-optional.ts ├── entries.ts ├── exact.ts ├── except.ts ├── exclude-strict.ts ├── extends-strict.ts ├── extract-strict.ts ├── find-global-type.ts ├── fixed-length-array.ts ├── get.ts ├── global-this.ts ├── greater-than-or-equal.ts ├── greater-than.ts ├── has-optional-keys.ts ├── has-readonly-keys.ts ├── has-required-keys.ts ├── has-writable-keys.ts ├── if-any.ts ├── if-never.ts ├── if-unknown.ts ├── if.ts ├── includes.ts ├── int-closed-range.ts ├── int-range.ts ├── internal │ ├── apply-default-options.ts │ ├── build-tuple.ts │ ├── collapse-literals-in-union.ts │ ├── collapse-rest-element.ts │ ├── has-multiple-call-signatures.ts │ ├── homomorphic-pick.ts │ ├── if-not-any-or-never.ts │ ├── is-array-readonly.ts │ ├── is-not-false.ts │ ├── is-number-like.ts │ ├── is-numeric.ts │ ├── is-whitespace.ts │ ├── not.ts │ ├── number-absolute.ts │ ├── object-value.ts │ ├── readonly-keys-of-union.ts │ ├── require-none.ts │ ├── tuple-max.ts │ ├── tuple-min.ts │ └── value-of-union.ts ├── invariant-of.ts ├── is-any.ts ├── is-equal.ts ├── is-float.ts ├── is-integer.ts ├── is-literal.ts ├── is-lowercase.ts ├── is-never.ts ├── is-null.ts ├── is-nullable.ts ├── is-optional.ts ├── is-tuple.ts ├── is-union.ts ├── is-unknown.ts ├── is-uppercase.ts ├── iterable-element.ts ├── join.ts ├── jsonifiable.ts ├── jsonify.ts ├── kebab-case.ts ├── kebab-cased-properties-deep.ts ├── kebab-cased-properties.ts ├── key-as-string.ts ├── keys-of-union.ts ├── last-array-element.ts ├── less-than-or-equal.ts ├── less-than.ts ├── literal-to-primitive-deep.ts ├── literal-to-primitive.ts ├── merge-deep.ts ├── merge-exclusive.ts ├── merge.ts ├── multidimensional-array.ts ├── multidimensional-readonly-array.ts ├── non-empty-object.ts ├── non-empty-string.ts ├── non-empty-tuple.ts ├── numeric.ts ├── observable-like.ts ├── omit-deep.ts ├── omit-index-signature.ts ├── opaque.ts ├── optional-keys-of.ts ├── or.ts ├── override-properties.ts ├── package-json.ts ├── partial-deep.ts ├── partial-on-undefined-deep.ts ├── pascal-case.ts ├── pascal-cased-properties-deep.ts ├── pascal-cased-properties.ts ├── paths.ts ├── pick-deep.ts ├── pick-index-signature.ts ├── promisable.ts ├── readonly-deep.ts ├── readonly-keys-of.ts ├── readonly-tuple.ts ├── remove-prefix.ts ├── replace.ts ├── require-all-or-none.ts ├── require-at-least-one.ts ├── require-exactly-one.ts ├── require-one-or-none.ts ├── required-deep.ts ├── required-keys-of.ts ├── schema.ts ├── screaming-snake-case.ts ├── set-field-type.ts ├── set-non-nullable-deep.ts ├── set-non-nullable.ts ├── set-optional.ts ├── set-parameter-type.ts ├── set-readonly.ts ├── set-required-deep.ts ├── set-required.ts ├── set-return-type.ts ├── shared-union-fields-deep.ts ├── shared-union-fields.ts ├── simplify-deep.ts ├── simplify.ts ├── single-key-object.ts ├── snake-case.ts ├── snake-cased-properties-deep.ts ├── snake-cased-properties.ts ├── split.ts ├── spread.ts ├── string-repeat.ts ├── string-slice.ts ├── stringified.ts ├── structured-cloneable.ts ├── subtract.ts ├── sum.ts ├── tagged-union.ts ├── trim.ts ├── ts41.ts ├── tsconfig-json.ts ├── tuple-to-object.ts ├── tuple-to-union.ts ├── undefined-on-partial-deep.ts ├── union-to-intersection.ts ├── union-to-tuple.ts ├── unknown-array.ts ├── unknown-map.ts ├── unknown-record.ts ├── unknown-set.ts ├── value-of.ts ├── words.ts ├── writable-deep.ts ├── writable-keys-of.ts └── writable.ts ├── tsconfig.json └── xo.config.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = tab 5 | end_of_line = lf 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | 10 | [*.yml] 11 | indent_style = space 12 | indent_size = 2 13 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/1 new type.yml: -------------------------------------------------------------------------------- 1 | name: 💡 Suggest new type 2 | description: ' ' # Magic whitespace to hide this required field 3 | labels: 'type addition' 4 | 5 | body: 6 | - type: textarea 7 | id: description 8 | validations: 9 | required: true 10 | attributes: 11 | label: Type description + examples 12 | 13 | - type: textarea 14 | id: type 15 | attributes: 16 | label: Type source 17 | description: If you already have the type source, enter it here as a starting point for a the discussion 18 | 19 | - id: requirements 20 | type: checkboxes 21 | attributes: 22 | label: Search existing types and issues first 23 | options: 24 | - label: I tried my best to look for it 25 | required: true 26 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/2 bug report.yml: -------------------------------------------------------------------------------- 1 | name: 🐛 Report bug 2 | description: ' ' # Magic whitespace to hide this required field 3 | labels: bug 4 | 5 | body: 6 | - type: textarea 7 | id: description 8 | validations: 9 | required: true 10 | attributes: 11 | label: Bug description 12 | 13 | - type: input 14 | id: repro 15 | validations: 16 | required: true 17 | attributes: 18 | label: Repro 19 | description: | 20 | Open [this playground](https://www.typescriptlang.org/play/?#code/JYWwDg9gTgLgBDAnmApnA3gUQB4GMVgwC+cAZlBCHAORKoC0pKAzjNQNwCwAUD3WgDEIEOAF4MPAJABXAHbBc0WQC44rKMFkBzLt0lQAhpoBGEAO6rTEADYoDs3UV19kaAHIQASkdmmzYuBx8QgAeIQgAGhpDE3NqAD5dOGS4AHpUuAA9AH4eIA), write a piece of code that fails your expectations, click "Share", and paste the URL here 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/3 enhancement.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: ♻️ Propose change or improvement to existing types 3 | about: ' ' # Magic whitespace to hide this required field 4 | labels: 'enhancement' 5 | --- 6 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: 'github-actions' 4 | directory: '/' 5 | schedule: 6 | interval: 'weekly' 7 | groups: 8 | github-actions: 9 | patterns: 10 | - '*' 11 | - package-ecosystem: 'npm' 12 | directory: '/' 13 | schedule: 14 | interval: 'monthly' 15 | versioning-strategy: 'increase-if-necessary' 16 | groups: 17 | development-dependencies: 18 | dependency-type: 'development' 19 | -------------------------------------------------------------------------------- /.github/funding.yml: -------------------------------------------------------------------------------- 1 | github: [sindresorhus, som-sm, Emiyaaaaa, voxpelli] 2 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | <!-- 2 | 3 | Thanks for submitting a pull request 🙌 4 | 5 | If you're submitting a new type, please review the contribution guidelines: 6 | https://github.com/sindresorhus/type-fest/blob/main/.github/contributing.md 7 | 8 | --> 9 | -------------------------------------------------------------------------------- /.github/security.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security). Tidelift will coordinate the fix and disclosure. 4 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | push: 4 | branches: 5 | - main 6 | tags: 7 | - '*' 8 | pull_request: 9 | branches: 10 | - main 11 | jobs: 12 | test: 13 | name: Node.js ${{ matrix.node-version }} 14 | runs-on: ubuntu-latest 15 | strategy: 16 | fail-fast: false 17 | matrix: 18 | node-version: 19 | - 24 20 | - 22 21 | - 20 22 | steps: 23 | - uses: actions/checkout@v4 24 | - uses: actions/setup-node@v4 25 | with: 26 | node-version: ${{ matrix.node-version }} 27 | - run: npm install 28 | - run: npm test 29 | types: 30 | name: TypeScript ${{ matrix.typescript-version }} 31 | runs-on: ubuntu-latest 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | typescript-version: 36 | - 'latest' 37 | - '~5.8.0' 38 | steps: 39 | - uses: actions/checkout@v4 40 | - uses: actions/setup-node@v4 41 | with: 42 | node-version: 20 43 | - run: npm install 44 | - run: npm install typescript@${{ matrix.typescript-version }} 45 | - run: npx tsc 46 | test-export: 47 | name: Test Module Export 48 | runs-on: ubuntu-latest 49 | steps: 50 | - uses: actions/checkout@v4 51 | - uses: actions/setup-node@v4 52 | with: 53 | node-version: 20 54 | - run: npm install rollup rollup-plugin-dts 55 | - run: npx rollup index.d.ts -p rollup-plugin-dts -d temp 56 | -------------------------------------------------------------------------------- /.github/workflows/ts-canary.yml: -------------------------------------------------------------------------------- 1 | name: TypeScript Canary 2 | on: 3 | schedule: 4 | # Every Thursday at 21.15 5 | - cron: '15 21 * * 4' 6 | workflow_dispatch: 7 | jobs: 8 | types: 9 | name: TypeScript ${{ matrix.typescript-version }} 10 | runs-on: ubuntu-latest 11 | strategy: 12 | fail-fast: false 13 | matrix: 14 | typescript-version: 15 | - next 16 | - latest 17 | steps: 18 | - uses: actions/checkout@v4 19 | - uses: actions/setup-node@v4 20 | with: 21 | node-version: lts/* 22 | - run: npm install 23 | - run: npm install typescript@${{ matrix.typescript-version }} 24 | - name: show installed typescript version 25 | run: npm list typescript --depth=0 26 | - run: npx tsc 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | yarn.lock 3 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /license-mit: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (https://sindresorhus.com) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /lint-rules/import-path.js: -------------------------------------------------------------------------------- 1 | import path from 'node:path'; 2 | 3 | export const importPathRule = /** @type {const} */ ({ 4 | meta: { 5 | type: 'problem', 6 | docs: { 7 | description: 'Enforces import paths to end with a \'.d.ts\' extension.', 8 | }, 9 | fixable: 'code', 10 | messages: { 11 | incorrectImportPath: 12 | 'Import path \'{{importPath}}\' must end with a \'.d.ts\' extension. Use \'{{fixedImportPath}}\' instead.', 13 | }, 14 | schema: [], 15 | }, 16 | defaultOptions: [], 17 | create(context) { 18 | return { 19 | ImportDeclaration(node) { 20 | const importPath = node.source.value; 21 | 22 | // Skip if not relative path 23 | if (!(importPath.startsWith('./') || importPath.startsWith('../'))) { 24 | return; 25 | } 26 | 27 | const filename = path.basename(importPath); 28 | const firstDotIndex = filename.indexOf('.'); 29 | const extension = firstDotIndex === -1 ? '' : filename.slice(firstDotIndex); 30 | 31 | // Skip if the import path already ends with `.d.ts` 32 | if (extension === '.d.ts') { 33 | return; 34 | } 35 | 36 | const importPathWithoutExtension = extension.length > 0 37 | ? importPath.slice(0, -extension.length) 38 | : importPath; 39 | const fixedImportPath = `${importPathWithoutExtension}.d.ts`; 40 | 41 | context.report({ 42 | node: node.source, 43 | messageId: 'incorrectImportPath', 44 | fix(fixer) { 45 | return fixer.replaceText(node.source, `'${fixedImportPath}'`); 46 | }, 47 | data: {importPath, fixedImportPath}, 48 | }); 49 | }, 50 | }; 51 | }, 52 | }); 53 | -------------------------------------------------------------------------------- /lint-rules/source-files-extension.js: -------------------------------------------------------------------------------- 1 | import path from 'node:path'; 2 | 3 | export const sourceFilesExtensionRule = /** @type {const} */ ({ 4 | meta: { 5 | type: 'problem', 6 | docs: { 7 | description: 'Enforces source files to end with a \'.d.ts\' extension.', 8 | }, 9 | fixable: 'code', 10 | messages: { 11 | incorrectFilename: 12 | 'Filename \'{{filename}}\' must end with a \'.d.ts\' extension. Use \'{{fixedFilename}}\' instead.', 13 | }, 14 | schema: [], 15 | }, 16 | defaultOptions: [], 17 | create(context) { 18 | const filename = path.basename(context.filename); 19 | const firstDotIndex = filename.indexOf('.'); 20 | const extension = firstDotIndex === -1 ? '' : filename.slice(firstDotIndex); 21 | 22 | if (extension === '.d.ts') { 23 | return {}; 24 | } 25 | 26 | return { 27 | Program(node) { 28 | const filenameWithoutExtension = extension.length > 0 29 | ? filename.slice(0, -extension.length) 30 | : filename; 31 | const fixedFilename = `${filenameWithoutExtension}.d.ts`; 32 | 33 | context.report({ 34 | loc: {column: 0, line: 1}, // Report error on start of the file 35 | node, 36 | messageId: 'incorrectFilename', 37 | data: {filename, fixedFilename}, 38 | }); 39 | }, 40 | }; 41 | }, 42 | }); 43 | -------------------------------------------------------------------------------- /media/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sindresorhus/type-fest/4d7cc50fbec6ae631fca98ae8d7e498f0d5f61ab/media/logo.png -------------------------------------------------------------------------------- /media/logo.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sindresorhus/type-fest/4d7cc50fbec6ae631fca98ae8d7e498f0d5f61ab/media/logo.sketch -------------------------------------------------------------------------------- /media/logo@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sindresorhus/type-fest/4d7cc50fbec6ae631fca98ae8d7e498f0d5f61ab/media/logo@2x.png -------------------------------------------------------------------------------- /media/readme.md: -------------------------------------------------------------------------------- 1 | # Media 2 | 3 | ## Attribution 4 | 5 | ### Fireworks vector graphic 6 | 7 | [Free Vectors via Vecteezy!](https://www.vecteezy.com) 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "type-fest", 3 | "version": "4.41.0", 4 | "description": "A collection of essential TypeScript types", 5 | "license": "(MIT OR CC0-1.0)", 6 | "repository": "sindresorhus/type-fest", 7 | "funding": "https://github.com/sponsors/sindresorhus", 8 | "author": { 9 | "name": "Sindre Sorhus", 10 | "email": "sindresorhus@gmail.com", 11 | "url": "https://sindresorhus.com" 12 | }, 13 | "type": "module", 14 | "exports": { 15 | ".": { 16 | "types": "./index.d.ts" 17 | }, 18 | "./globals": { 19 | "types": "./source/globals/index.d.ts" 20 | } 21 | }, 22 | "types": "./index.d.ts", 23 | "sideEffects": false, 24 | "engines": { 25 | "node": ">=20" 26 | }, 27 | "scripts": { 28 | "test:tsc": "tsc", 29 | "test:tsd": "tsd", 30 | "test:xo": "xo", 31 | "test": "run-p test:*" 32 | }, 33 | "files": [ 34 | "index.d.ts", 35 | "source", 36 | "license-mit", 37 | "license-cc0" 38 | ], 39 | "keywords": [ 40 | "typescript", 41 | "ts", 42 | "types", 43 | "utility", 44 | "util", 45 | "utilities", 46 | "omit", 47 | "merge", 48 | "json", 49 | "generics" 50 | ], 51 | "dependencies": { 52 | "tagged-tag": "^1.0.0" 53 | }, 54 | "devDependencies": { 55 | "@sindresorhus/tsconfig": "^7.0.0", 56 | "expect-type": "^1.2.1", 57 | "npm-run-all2": "^8.0.1", 58 | "tsd": "^0.32.0", 59 | "typescript": "~5.8.3", 60 | "xo": "^1.0.5" 61 | }, 62 | "tsd": { 63 | "compilerOptions": { 64 | "noUnusedLocals": false 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /source/array-indices.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | Provides valid indices for a constant array or tuple. 3 | 4 | Use-case: This type is useful when working with constant arrays or tuples and you want to enforce type-safety for accessing elements by their indices. 5 | 6 | @example 7 | ``` 8 | import type {ArrayIndices, ArrayValues} from 'type-fest'; 9 | 10 | const weekdays = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'] as const; 11 | 12 | type Weekday = ArrayIndices<typeof weekdays>; 13 | type WeekdayName = ArrayValues<typeof weekdays>; 14 | 15 | const getWeekdayName = (day: Weekday): WeekdayName => weekdays[day]; 16 | ``` 17 | 18 | @see {@link ArrayValues} 19 | 20 | @category Array 21 | */ 22 | export type ArrayIndices<Element extends readonly unknown[]> = 23 | Exclude<Partial<Element>['length'], Element['length']>; 24 | -------------------------------------------------------------------------------- /source/array-values.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | Provides all values for a constant array or tuple. 3 | 4 | Use-case: This type is useful when working with constant arrays or tuples and you want to enforce type-safety with their values. 5 | 6 | @example 7 | ``` 8 | import type {ArrayValues, ArrayIndices} from 'type-fest'; 9 | 10 | const weekdays = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'] as const; 11 | 12 | type WeekdayName = ArrayValues<typeof weekdays>; 13 | type Weekday = ArrayIndices<typeof weekdays>; 14 | 15 | const getWeekdayName = (day: Weekday): WeekdayName => weekdays[day]; 16 | ``` 17 | 18 | @see {@link ArrayIndices} 19 | 20 | @category Array 21 | */ 22 | export type ArrayValues<T extends readonly unknown[]> = T[number]; 23 | -------------------------------------------------------------------------------- /source/arrayable.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | Create a type that represents either the value or an array of the value. 3 | 4 | @see Promisable 5 | 6 | @example 7 | ``` 8 | import type {Arrayable} from 'type-fest'; 9 | 10 | function bundle(input: string, output: Arrayable<string>) { 11 | const outputList = Array.isArray(output) ? output : [output]; 12 | 13 | // … 14 | 15 | for (const output of outputList) { 16 | console.log(`write to: ${output}`); 17 | } 18 | } 19 | 20 | bundle('src/index.js', 'dist/index.js'); 21 | bundle('src/index.js', ['dist/index.cjs', 'dist/index.mjs']); 22 | ``` 23 | 24 | @category Array 25 | */ 26 | export type Arrayable<T> = 27 | T 28 | // TODO: Use `readonly T[]` when this issue is resolved: https://github.com/microsoft/TypeScript/issues/17002 29 | | T[]; 30 | -------------------------------------------------------------------------------- /source/async-return-type.d.ts: -------------------------------------------------------------------------------- 1 | type AsyncFunction = (...arguments_: any[]) => PromiseLike<unknown>; 2 | 3 | /** 4 | Unwrap the return type of a function that returns a `Promise`. 5 | 6 | There has been [discussion](https://github.com/microsoft/TypeScript/pull/35998) about implementing this type in TypeScript. 7 | 8 | @example 9 | ```ts 10 | import type {AsyncReturnType} from 'type-fest'; 11 | import {asyncFunction} from 'api'; 12 | 13 | // This type resolves to the unwrapped return type of `asyncFunction`. 14 | type Value = AsyncReturnType<typeof asyncFunction>; 15 | 16 | async function doSomething(value: Value) {} 17 | 18 | asyncFunction().then(value => doSomething(value)); 19 | ``` 20 | 21 | @category Async 22 | */ 23 | export type AsyncReturnType<Target extends AsyncFunction> = Awaited<ReturnType<Target>>; 24 | -------------------------------------------------------------------------------- /source/asyncify.d.ts: -------------------------------------------------------------------------------- 1 | import type {SetReturnType} from './set-return-type.d.ts'; 2 | 3 | /** 4 | Create an async version of the given function type, by boxing the return type in `Promise` while keeping the same parameter types. 5 | 6 | Use-case: You have two functions, one synchronous and one asynchronous that do the same thing. Instead of having to duplicate the type definition, you can use `Asyncify` to reuse the synchronous type. 7 | 8 | @example 9 | ``` 10 | import type {Asyncify} from 'type-fest'; 11 | 12 | // Synchronous function. 13 | function getFooSync(someArg: SomeType): Foo { 14 | // … 15 | } 16 | 17 | type AsyncifiedFooGetter = Asyncify<typeof getFooSync>; 18 | //=> type AsyncifiedFooGetter = (someArg: SomeType) => Promise<Foo>; 19 | 20 | // Same as `getFooSync` but asynchronous. 21 | const getFooAsync: AsyncifiedFooGetter = (someArg) => { 22 | // TypeScript now knows that `someArg` is `SomeType` automatically. 23 | // It also knows that this function must return `Promise<Foo>`. 24 | // If you have `@typescript-eslint/promise-function-async` linter rule enabled, it will even report that "Functions that return promises must be async.". 25 | 26 | // … 27 | } 28 | ``` 29 | 30 | @category Async 31 | */ 32 | export type Asyncify<Function_ extends (...arguments_: any[]) => any> = SetReturnType<Function_, Promise<Awaited<ReturnType<Function_>>>>; 33 | -------------------------------------------------------------------------------- /source/basic.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | Matches a [`class`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes). 3 | 4 | @category Class 5 | */ 6 | export type Class<T, Arguments extends unknown[] = any[]> = { 7 | prototype: Pick<T, keyof T>; 8 | new(...arguments_: Arguments): T; 9 | }; 10 | 11 | /** 12 | Matches a [`class` constructor](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes). 13 | 14 | @category Class 15 | */ 16 | export type Constructor<T, Arguments extends unknown[] = any[]> = new(...arguments_: Arguments) => T; 17 | 18 | /** 19 | Matches an [`abstract class`](https://www.typescriptlang.org/docs/handbook/2/classes.html#abstract-classes-and-members). 20 | 21 | @category Class 22 | 23 | @privateRemarks 24 | We cannot use a `type` here because TypeScript throws: 'abstract' modifier cannot appear on a type member. (1070) 25 | */ 26 | // eslint-disable-next-line @typescript-eslint/consistent-type-definitions 27 | export interface AbstractClass<T, Arguments extends unknown[] = any[]> extends AbstractConstructor<T, Arguments> { 28 | prototype: Pick<T, keyof T>; 29 | } 30 | 31 | /** 32 | Matches an [`abstract class`](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-2.html#abstract-construct-signatures) constructor. 33 | 34 | @category Class 35 | */ 36 | export type AbstractConstructor<T, Arguments extends unknown[] = any[]> = abstract new(...arguments_: Arguments) => T; 37 | -------------------------------------------------------------------------------- /source/camel-cased-properties.d.ts: -------------------------------------------------------------------------------- 1 | import type {CamelCase, CamelCaseOptions, DefaultCamelCaseOptions} from './camel-case.d.ts'; 2 | import type {ApplyDefaultOptions} from './internal/index.d.ts'; 3 | 4 | /** 5 | Convert object properties to camel case but not recursively. 6 | 7 | This can be useful when, for example, converting some API types from a different style. 8 | 9 | @see CamelCasedPropertiesDeep 10 | @see CamelCase 11 | 12 | @example 13 | ``` 14 | import type {CamelCasedProperties} from 'type-fest'; 15 | 16 | interface User { 17 | UserId: number; 18 | UserName: string; 19 | } 20 | 21 | const result: CamelCasedProperties<User> = { 22 | userId: 1, 23 | userName: 'Tom', 24 | }; 25 | 26 | const preserveConsecutiveUppercase: CamelCasedProperties<{fooBAR: string}, {preserveConsecutiveUppercase: true}> = { 27 | fooBAR: 'string', 28 | }; 29 | ``` 30 | 31 | @category Change case 32 | @category Template literal 33 | @category Object 34 | */ 35 | export type CamelCasedProperties<Value, Options extends CamelCaseOptions = {}> = Value extends Function 36 | ? Value 37 | : Value extends Array<infer U> 38 | ? Value 39 | : { 40 | [K in keyof Value as 41 | CamelCase<K, ApplyDefaultOptions<CamelCaseOptions, DefaultCamelCaseOptions, Options>> 42 | ]: Value[K]; 43 | }; 44 | -------------------------------------------------------------------------------- /source/conditional-except.d.ts: -------------------------------------------------------------------------------- 1 | import type {Except} from './except.d.ts'; 2 | import type {ConditionalKeys} from './conditional-keys.d.ts'; 3 | 4 | /** 5 | Exclude keys from a shape that matches the given `Condition`. 6 | 7 | This is useful when you want to create a new type with a specific set of keys from a shape. For example, you might want to exclude all the primitive properties from a class and form a new shape containing everything but the primitive properties. 8 | 9 | @example 10 | ``` 11 | import type {Primitive, ConditionalExcept} from 'type-fest'; 12 | 13 | class Awesome { 14 | name: string; 15 | successes: number; 16 | failures: bigint; 17 | 18 | run() {} 19 | } 20 | 21 | type ExceptPrimitivesFromAwesome = ConditionalExcept<Awesome, Primitive>; 22 | //=> {run: () => void} 23 | ``` 24 | 25 | @example 26 | ``` 27 | import type {ConditionalExcept} from 'type-fest'; 28 | 29 | interface Example { 30 | a: string; 31 | b: string | number; 32 | c: () => void; 33 | d: {}; 34 | } 35 | 36 | type NonStringKeysOnly = ConditionalExcept<Example, string>; 37 | //=> {b: string | number; c: () => void; d: {}} 38 | ``` 39 | 40 | @category Object 41 | */ 42 | export type ConditionalExcept<Base, Condition> = Except< 43 | Base, 44 | ConditionalKeys<Base, Condition> 45 | >; 46 | -------------------------------------------------------------------------------- /source/conditional-pick.d.ts: -------------------------------------------------------------------------------- 1 | import type {ConditionalKeys} from './conditional-keys.d.ts'; 2 | 3 | /** 4 | Pick keys from the shape that matches the given `Condition`. 5 | 6 | This is useful when you want to create a new type from a specific subset of an existing type. For example, you might want to pick all the primitive properties from a class and form a new automatically derived type. 7 | 8 | @example 9 | ``` 10 | import type {Primitive, ConditionalPick} from 'type-fest'; 11 | 12 | class Awesome { 13 | name: string; 14 | successes: number; 15 | failures: bigint; 16 | 17 | run() {} 18 | } 19 | 20 | type PickPrimitivesFromAwesome = ConditionalPick<Awesome, Primitive>; 21 | //=> {name: string; successes: number; failures: bigint} 22 | ``` 23 | 24 | @example 25 | ``` 26 | import type {ConditionalPick} from 'type-fest'; 27 | 28 | interface Example { 29 | a: string; 30 | b: string | number; 31 | c: () => void; 32 | d: {}; 33 | } 34 | 35 | type StringKeysOnly = ConditionalPick<Example, string>; 36 | //=> {a: string} 37 | ``` 38 | 39 | @category Object 40 | */ 41 | export type ConditionalPick<Base, Condition> = Pick< 42 | Base, 43 | ConditionalKeys<Base, Condition> 44 | >; 45 | -------------------------------------------------------------------------------- /source/conditional-simplify-deep.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | Recursively simplifies a type while including and/or excluding certain types from being simplified. 3 | 4 | @example 5 | ``` 6 | import type {ConditionalSimplifyDeep} from 'type-fest'; 7 | 8 | type TypeA = { 9 | foo: { 10 | a: string; 11 | }; 12 | }; 13 | 14 | type TypeB = { 15 | foo: { 16 | b: string; 17 | }; 18 | }; 19 | 20 | type SimplifyDeepTypeAB = ConditionalSimplifyDeep<TypeA & TypeB, never, object>; 21 | //=> {foo: {a: string; b: string}} 22 | ``` 23 | 24 | @example 25 | ``` 26 | import type {ConditionalSimplifyDeep} from 'type-fest'; 27 | 28 | type SomeComplexType1 = { 29 | a1: string; 30 | b1: number; 31 | c1: boolean; 32 | }; 33 | 34 | type SomeComplexType2 = { 35 | a2: string; 36 | b2: number; 37 | c2: boolean; 38 | }; 39 | 40 | type TypeA = { 41 | foo: { 42 | a: string; 43 | complexType: SomeComplexType1; 44 | }; 45 | }; 46 | 47 | type TypeB = { 48 | foo: { 49 | b: string; 50 | complexType: SomeComplexType2; 51 | }; 52 | }; 53 | 54 | type SimplifyDeepTypeAB = ConditionalSimplifyDeep<TypeA & TypeB, SomeComplexType1 | SomeComplexType2, object>; 55 | //=> { 56 | // foo: { 57 | // a: string; 58 | // b: string; 59 | // complexType: SomeComplexType1 & SomeComplexType2; 60 | // }; 61 | // } 62 | ``` 63 | 64 | @see SimplifyDeep 65 | @category Object 66 | */ 67 | export type ConditionalSimplifyDeep<Type, ExcludeType = never, IncludeType = unknown> = Type extends ExcludeType 68 | ? Type 69 | : Type extends IncludeType 70 | ? {[TypeKey in keyof Type]: ConditionalSimplifyDeep<Type[TypeKey], ExcludeType, IncludeType>} 71 | : Type; 72 | -------------------------------------------------------------------------------- /source/conditional-simplify.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | Simplifies a type while including and/or excluding certain types from being simplified. 3 | 4 | Useful to improve type hints shown in editors. And also to transform an `interface` into a `type` to aid with assignability. 5 | 6 | @example 7 | ``` 8 | import type {ConditionalSimplify} from 'type-fest'; 9 | 10 | type TypeA = { 11 | a: string; 12 | }; 13 | 14 | type TypeB = { 15 | b: string; 16 | }; 17 | 18 | type TypeAB = TypeA & TypeB; 19 | //=> TypeA & TypeB 20 | 21 | type SimplifyTypeAB = ConditionalSimplify<TypeAB, never, object>; 22 | //=> {a: string; b: string} 23 | ``` 24 | 25 | @example 26 | ``` 27 | import type {ConditionalSimplify} from 'type-fest'; 28 | 29 | type Simplify<T> = ConditionalSimplify<T, Set<unknown> | Map<unknown, unknown> | unknown[], object>; 30 | 31 | type A = Simplify<Set<number> & Set<string>>; 32 | //=> Set<number> & Set<string> 33 | 34 | type B = Simplify<Map<number, number> & Map<string, string>>; 35 | //=> Map<number, number> & Map<string, string> 36 | 37 | type C = Simplify<{a: number} & {b: string}>; 38 | //=> {a: number; b: string} 39 | ``` 40 | 41 | @see ConditionalSimplifyDeep 42 | @category Object 43 | */ 44 | export type ConditionalSimplify<Type, ExcludeType = never, IncludeType = unknown> = Type extends ExcludeType 45 | ? Type 46 | : Type extends IncludeType 47 | ? {[TypeKey in keyof Type]: Type[TypeKey]} 48 | : Type; 49 | -------------------------------------------------------------------------------- /source/delimiter-cased-properties.d.ts: -------------------------------------------------------------------------------- 1 | import type {DefaultDelimiterCaseOptions, DelimiterCase} from './delimiter-case.d.ts'; 2 | import type {ApplyDefaultOptions} from './internal/index.d.ts'; 3 | import type {WordsOptions} from './words.d.ts'; 4 | 5 | /** 6 | Convert object properties to delimiter case but not recursively. 7 | 8 | This can be useful when, for example, converting some API types from a different style. 9 | 10 | @see DelimiterCase 11 | @see DelimiterCasedPropertiesDeep 12 | 13 | @example 14 | ``` 15 | import type {DelimiterCasedProperties} from 'type-fest'; 16 | 17 | interface User { 18 | userId: number; 19 | userName: string; 20 | } 21 | 22 | const result: DelimiterCasedProperties<User, '-'> = { 23 | 'user-id': 1, 24 | 'user-name': 'Tom', 25 | }; 26 | 27 | const splitOnNumbers: DelimiterCasedProperties<{line1: string}, '-', {splitOnNumbers: true}> = { 28 | 'line-1': 'string', 29 | }; 30 | ``` 31 | 32 | @category Change case 33 | @category Template literal 34 | @category Object 35 | */ 36 | export type DelimiterCasedProperties< 37 | Value, 38 | Delimiter extends string, 39 | Options extends WordsOptions = {}, 40 | > = Value extends Function 41 | ? Value 42 | : Value extends Array<infer U> 43 | ? Value 44 | : {[K in keyof Value as 45 | DelimiterCase<K, Delimiter, ApplyDefaultOptions<WordsOptions, DefaultDelimiterCaseOptions, Options>> 46 | ]: Value[K]}; 47 | -------------------------------------------------------------------------------- /source/empty-object.d.ts: -------------------------------------------------------------------------------- 1 | declare const emptyObjectSymbol: unique symbol; 2 | 3 | /** 4 | Represents a strictly empty plain object, the `{}` value. 5 | 6 | When you annotate something as the type `{}`, it can be anything except `null` and `undefined`. This means that you cannot use `{}` to represent an empty plain object ([read more](https://stackoverflow.com/questions/47339869/typescript-empty-object-and-any-difference/52193484#52193484)). 7 | 8 | @example 9 | ``` 10 | import type {EmptyObject} from 'type-fest'; 11 | 12 | // The following illustrates the problem with `{}`. 13 | const foo1: {} = {}; // Pass 14 | const foo2: {} = []; // Pass 15 | const foo3: {} = 42; // Pass 16 | const foo4: {} = {a: 1}; // Pass 17 | 18 | // With `EmptyObject` only the first case is valid. 19 | const bar1: EmptyObject = {}; // Pass 20 | const bar2: EmptyObject = 42; // Fail 21 | const bar3: EmptyObject = []; // Fail 22 | const bar4: EmptyObject = {a: 1}; // Fail 23 | ``` 24 | 25 | Unfortunately, `Record<string, never>`, `Record<keyof any, never>` and `Record<never, never>` do not work. See {@link https://github.com/sindresorhus/type-fest/issues/395 #395}. 26 | 27 | @category Object 28 | */ 29 | export type EmptyObject = {[emptyObjectSymbol]?: never}; 30 | 31 | /** 32 | Returns a `boolean` for whether the type is strictly equal to an empty plain object, the `{}` value. 33 | 34 | @example 35 | ``` 36 | import type {IsEmptyObject} from 'type-fest'; 37 | 38 | type Pass = IsEmptyObject<{}>; //=> true 39 | type Fail = IsEmptyObject<[]>; //=> false 40 | type Fail = IsEmptyObject<null>; //=> false 41 | ``` 42 | 43 | @see EmptyObject 44 | @category Object 45 | */ 46 | export type IsEmptyObject<T> = T extends EmptyObject ? true : false; 47 | -------------------------------------------------------------------------------- /source/enforce-optional.d.ts: -------------------------------------------------------------------------------- 1 | import type {Simplify} from './simplify.d.ts'; 2 | 3 | // Returns `never` if the key is optional otherwise return the key type. 4 | type RequiredFilter<Type, Key extends keyof Type> = undefined extends Type[Key] 5 | ? Type[Key] extends undefined 6 | ? Key 7 | : never 8 | : Key; 9 | 10 | // Returns `never` if the key is required otherwise return the key type. 11 | type OptionalFilter<Type, Key extends keyof Type> = undefined extends Type[Key] 12 | ? Type[Key] extends undefined 13 | ? never 14 | : Key 15 | : never; 16 | 17 | /** 18 | Enforce optional keys (by adding the `?` operator) for keys that have a union with `undefined`. 19 | 20 | @example 21 | ``` 22 | import type {EnforceOptional} from 'type-fest'; 23 | 24 | type Foo = { 25 | a: string; 26 | b?: string; 27 | c: undefined; 28 | d: number | undefined; 29 | }; 30 | 31 | type FooBar = EnforceOptional<Foo>; 32 | // => { 33 | // a: string; 34 | // b?: string; 35 | // c: undefined; 36 | // d?: number; 37 | // } 38 | ``` 39 | 40 | @internal 41 | @category Object 42 | */ 43 | export type EnforceOptional<ObjectType> = Simplify<{ 44 | [Key in keyof ObjectType as RequiredFilter<ObjectType, Key>]: ObjectType[Key] 45 | } & { 46 | [Key in keyof ObjectType as OptionalFilter<ObjectType, Key>]?: Exclude<ObjectType[Key], undefined> 47 | }>; 48 | -------------------------------------------------------------------------------- /source/extends-strict.d.ts: -------------------------------------------------------------------------------- 1 | import type {IsNever} from './is-never.d.ts'; 2 | import type {IsAny} from './is-any.d.ts'; 3 | 4 | /** 5 | A stricter, non-distributive version of `extends` for checking whether one type is assignable to another. 6 | 7 | Unlike the built-in `extends` keyword, `ExtendsStrict`: 8 | 9 | 1. Prevents distribution over union types by wrapping both types in tuples. For example, `ExtendsStrict<string | number, number>` returns `false`, whereas `string | number extends number` would result in `boolean`. 10 | 11 | 2. Treats `never` as a special case: `never` doesn't extend every other type, it only extends itself (or `any`). For example, `ExtendsStrict<never, number>` returns `false` whereas `never extends number` would result in `true`. 12 | 13 | @example 14 | ``` 15 | import type {ExtendsStrict} from 'type-fest'; 16 | 17 | type T1 = ExtendsStrict<number | string, string>; 18 | //=> false 19 | 20 | type T2 = ExtendsStrict<never, number>; 21 | //=> false 22 | 23 | type T3 = ExtendsStrict<never, never>; 24 | //=> true 25 | 26 | type T4 = ExtendsStrict<string, number | string>; 27 | //=> true 28 | 29 | type T5 = ExtendsStrict<string, string>; 30 | //=> true 31 | ``` 32 | 33 | @category Improved Built-in 34 | */ 35 | export type ExtendsStrict<Left, Right> = 36 | IsAny<Left | Right> extends true 37 | ? true 38 | : IsNever<Left> extends true 39 | ? IsNever<Right> 40 | : [Left] extends [Right] 41 | ? true 42 | : false; 43 | -------------------------------------------------------------------------------- /source/global-this.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | Declare locally scoped properties on `globalThis`. 3 | 4 | When defining a global variable in a declaration file is inappropriate, it can be helpful to define a `type` or `interface` (say `ExtraGlobals`) with the global variable and then cast `globalThis` via code like `globalThis as unknown as ExtraGlobals`. 5 | 6 | Instead of casting through `unknown`, you can update your `type` or `interface` to extend `GlobalThis` and then directly cast `globalThis`. 7 | 8 | @example 9 | ``` 10 | import type {GlobalThis} from 'type-fest'; 11 | 12 | type ExtraGlobals = GlobalThis & { 13 | readonly GLOBAL_TOKEN: string; 14 | }; 15 | 16 | (globalThis as ExtraGlobals).GLOBAL_TOKEN; 17 | ``` 18 | 19 | @category Type 20 | */ 21 | export type GlobalThis = typeof globalThis; 22 | -------------------------------------------------------------------------------- /source/globals/index.d.ts: -------------------------------------------------------------------------------- 1 | export type * from './observable-like.d.ts'; 2 | -------------------------------------------------------------------------------- /source/greater-than-or-equal.d.ts: -------------------------------------------------------------------------------- 1 | import type {GreaterThan} from './greater-than.d.ts'; 2 | 3 | /** 4 | Returns a boolean for whether a given number is greater than or equal to another number. 5 | 6 | @example 7 | ``` 8 | import type {GreaterThanOrEqual} from 'type-fest'; 9 | 10 | GreaterThanOrEqual<1, -5>; 11 | //=> true 12 | 13 | GreaterThanOrEqual<1, 1>; 14 | //=> true 15 | 16 | GreaterThanOrEqual<1, 5>; 17 | //=> false 18 | ``` 19 | */ 20 | export type GreaterThanOrEqual<A extends number, B extends number> = number extends A | B 21 | ? never 22 | : A extends B ? true : GreaterThan<A, B>; 23 | -------------------------------------------------------------------------------- /source/has-optional-keys.d.ts: -------------------------------------------------------------------------------- 1 | import type {OptionalKeysOf} from './optional-keys-of.d.ts'; 2 | 3 | /** 4 | Creates a type that represents `true` or `false` depending on whether the given type has any optional fields. 5 | 6 | This is useful when you want to create an API whose behavior depends on the presence or absence of optional fields. 7 | 8 | @example 9 | ``` 10 | import type {HasOptionalKeys, OptionalKeysOf} from 'type-fest'; 11 | 12 | type UpdateService<Entity extends object> = { 13 | removeField: HasOptionalKeys<Entity> extends true 14 | ? (field: OptionalKeysOf<Entity>) => Promise<void> 15 | : never 16 | } 17 | ``` 18 | 19 | @category Utilities 20 | */ 21 | export type HasOptionalKeys<BaseType extends object> = OptionalKeysOf<BaseType> extends never ? false : true; 22 | -------------------------------------------------------------------------------- /source/has-readonly-keys.d.ts: -------------------------------------------------------------------------------- 1 | import type {ReadonlyKeysOf} from './readonly-keys-of.d.ts'; 2 | 3 | /** 4 | Creates a type that represents `true` or `false` depending on whether the given type has any readonly fields. 5 | 6 | This is useful when you want to create an API whose behavior depends on the presence or absence of readonly fields. 7 | 8 | @example 9 | ``` 10 | import type {HasReadonlyKeys, ReadonlyKeysOf} from 'type-fest'; 11 | 12 | type UpdateService<Entity extends object> = { 13 | removeField: HasReadonlyKeys<Entity> extends true 14 | ? (field: ReadonlyKeysOf<Entity>) => Promise<void> 15 | : never 16 | } 17 | ``` 18 | 19 | @category Utilities 20 | */ 21 | export type HasReadonlyKeys<BaseType extends object> = ReadonlyKeysOf<BaseType> extends never ? false : true; 22 | -------------------------------------------------------------------------------- /source/has-required-keys.d.ts: -------------------------------------------------------------------------------- 1 | import type {RequiredKeysOf} from './required-keys-of.d.ts'; 2 | 3 | /** 4 | Creates a type that represents `true` or `false` depending on whether the given type has any required fields. 5 | 6 | This is useful when you want to create an API whose behavior depends on the presence or absence of required fields. 7 | 8 | @example 9 | ``` 10 | import type {HasRequiredKeys} from 'type-fest'; 11 | 12 | type GeneratorOptions<Template extends object> = { 13 | prop1: number; 14 | prop2: string; 15 | } & (HasRequiredKeys<Template> extends true 16 | ? {template: Template} 17 | : {template?: Template}); 18 | 19 | interface Template1 { 20 | optionalSubParam?: string; 21 | } 22 | 23 | interface Template2 { 24 | requiredSubParam: string; 25 | } 26 | 27 | type Options1 = GeneratorOptions<Template1>; 28 | type Options2 = GeneratorOptions<Template2>; 29 | 30 | const optA: Options1 = { 31 | prop1: 0, 32 | prop2: 'hi' 33 | }; 34 | const optB: Options1 = { 35 | prop1: 0, 36 | prop2: 'hi', 37 | template: {} 38 | }; 39 | const optC: Options1 = { 40 | prop1: 0, 41 | prop2: 'hi', 42 | template: { 43 | optionalSubParam: 'optional value' 44 | } 45 | }; 46 | 47 | const optD: Options2 = { 48 | prop1: 0, 49 | prop2: 'hi', 50 | template: { 51 | requiredSubParam: 'required value' 52 | } 53 | }; 54 | 55 | ``` 56 | 57 | @category Utilities 58 | */ 59 | export type HasRequiredKeys<BaseType extends object> = RequiredKeysOf<BaseType> extends never ? false : true; 60 | -------------------------------------------------------------------------------- /source/has-writable-keys.d.ts: -------------------------------------------------------------------------------- 1 | import type {WritableKeysOf} from './writable-keys-of.d.ts'; 2 | 3 | /** 4 | Creates a type that represents `true` or `false` depending on whether the given type has any writable fields. 5 | 6 | This is useful when you want to create an API whose behavior depends on the presence or absence of writable fields. 7 | 8 | @example 9 | ``` 10 | import type {HasWritableKeys, WritableKeysOf} from 'type-fest'; 11 | 12 | type UpdateService<Entity extends object> = { 13 | removeField: HasWritableKeys<Entity> extends true 14 | ? (field: WritableKeysOf<Entity>) => Promise<void> 15 | : never 16 | } 17 | ``` 18 | 19 | @category Utilities 20 | */ 21 | export type HasWritableKeys<BaseType extends object> = WritableKeysOf<BaseType> extends never ? false : true; 22 | -------------------------------------------------------------------------------- /source/if-any.d.ts: -------------------------------------------------------------------------------- 1 | import type {IsAny} from './is-any.d.ts'; 2 | 3 | /** 4 | An if-else-like type that resolves depending on whether the given type is `any`. 5 | 6 | @deprecated This type will be removed in the next major version. Use the {@link If} type instead. 7 | 8 | @see {@link IsAny} 9 | 10 | @example 11 | ``` 12 | import type {IfAny} from 'type-fest'; 13 | 14 | type ShouldBeTrue = IfAny<any>; 15 | //=> true 16 | 17 | type ShouldBeBar = IfAny<'not any', 'foo', 'bar'>; 18 | //=> 'bar' 19 | ``` 20 | 21 | @category Type Guard 22 | @category Utilities 23 | */ 24 | export type IfAny<T, TypeIfAny = true, TypeIfNotAny = false> = ( 25 | IsAny<T> extends true ? TypeIfAny : TypeIfNotAny 26 | ); 27 | -------------------------------------------------------------------------------- /source/if-empty-object.d.ts: -------------------------------------------------------------------------------- 1 | import type {IsEmptyObject} from './empty-object.d.ts'; 2 | 3 | /** 4 | An if-else-like type that resolves depending on whether the given type is `{}`. 5 | 6 | @deprecated This type will be removed in the next major version. Use the {@link If} type instead. 7 | 8 | @see {@link IsEmptyObject} 9 | 10 | @example 11 | ``` 12 | import type {IfEmptyObject} from 'type-fest'; 13 | 14 | type ShouldBeTrue = IfEmptyObject<{}>; 15 | //=> true 16 | 17 | type ShouldBeBar = IfEmptyObject<{key: any}, 'foo', 'bar'>; 18 | //=> 'bar' 19 | ``` 20 | 21 | @category Type Guard 22 | @category Utilities 23 | */ 24 | export type IfEmptyObject< 25 | T, 26 | TypeIfEmptyObject = true, 27 | TypeIfNotEmptyObject = false, 28 | > = IsEmptyObject<T> extends true ? TypeIfEmptyObject : TypeIfNotEmptyObject; 29 | -------------------------------------------------------------------------------- /source/if-never.d.ts: -------------------------------------------------------------------------------- 1 | import type {IsNever} from './is-never.d.ts'; 2 | 3 | /** 4 | An if-else-like type that resolves depending on whether the given type is `never`. 5 | 6 | @deprecated This type will be removed in the next major version. Use the {@link If} type instead. 7 | 8 | @see {@link IsNever} 9 | 10 | @example 11 | ``` 12 | import type {IfNever} from 'type-fest'; 13 | 14 | type ShouldBeTrue = IfNever<never>; 15 | //=> true 16 | 17 | type ShouldBeBar = IfNever<'not never', 'foo', 'bar'>; 18 | //=> 'bar' 19 | ``` 20 | 21 | @category Type Guard 22 | @category Utilities 23 | */ 24 | export type IfNever<T, TypeIfNever = true, TypeIfNotNever = false> = ( 25 | IsNever<T> extends true ? TypeIfNever : TypeIfNotNever 26 | ); 27 | -------------------------------------------------------------------------------- /source/if-null.d.ts: -------------------------------------------------------------------------------- 1 | import type {IsNull} from './is-null.d.ts'; 2 | 3 | /** 4 | An if-else-like type that resolves depending on whether the given type is `null`. 5 | 6 | @deprecated This type will be removed in the next major version. Use the {@link If} type instead. 7 | 8 | @see {@link IsNull} 9 | 10 | @example 11 | ``` 12 | import type {IfNull} from 'type-fest'; 13 | 14 | type ShouldBeTrue = IfNull<null>; 15 | //=> true 16 | 17 | type ShouldBeBar = IfNull<'not null', 'foo', 'bar'>; 18 | //=> 'bar' 19 | ``` 20 | 21 | @category Type Guard 22 | @category Utilities 23 | */ 24 | export type IfNull<T, TypeIfNull = true, TypeIfNotNull = false> = ( 25 | IsNull<T> extends true ? TypeIfNull : TypeIfNotNull 26 | ); 27 | -------------------------------------------------------------------------------- /source/if-unknown.d.ts: -------------------------------------------------------------------------------- 1 | import type {IsUnknown} from './is-unknown.d.ts'; 2 | 3 | /** 4 | An if-else-like type that resolves depending on whether the given type is `unknown`. 5 | 6 | @deprecated This type will be removed in the next major version. Use the {@link If} type instead. 7 | 8 | @see {@link IsUnknown} 9 | 10 | @example 11 | ``` 12 | import type {IfUnknown} from 'type-fest'; 13 | 14 | type ShouldBeTrue = IfUnknown<unknown>; 15 | //=> true 16 | 17 | type ShouldBeBar = IfUnknown<'not unknown', 'foo', 'bar'>; 18 | //=> 'bar' 19 | ``` 20 | 21 | @category Type Guard 22 | @category Utilities 23 | */ 24 | export type IfUnknown<T, TypeIfUnknown = true, TypeIfNotUnknown = false> = ( 25 | IsUnknown<T> extends true ? TypeIfUnknown : TypeIfNotUnknown 26 | ); 27 | -------------------------------------------------------------------------------- /source/includes.d.ts: -------------------------------------------------------------------------------- 1 | import type {IsEqual} from './is-equal.d.ts'; 2 | 3 | /** 4 | Returns a boolean for whether the given array includes the given item. 5 | 6 | This can be useful if another type wants to make a decision based on whether the array includes that item. 7 | 8 | @example 9 | ``` 10 | import type {Includes} from 'type-fest'; 11 | 12 | type hasRed<array extends any[]> = Includes<array, 'red'>; 13 | ``` 14 | 15 | @category Array 16 | */ 17 | export type Includes<Value extends readonly any[], Item> = 18 | Value extends readonly [Value[0], ...infer rest] 19 | ? IsEqual<Value[0], Item> extends true 20 | ? true 21 | : Includes<rest, Item> 22 | : false; 23 | -------------------------------------------------------------------------------- /source/int-closed-range.d.ts: -------------------------------------------------------------------------------- 1 | import type {IntRange} from './int-range.d.ts'; 2 | import type {Sum} from './sum.d.ts'; 3 | 4 | /** 5 | Generate a union of numbers. 6 | 7 | The numbers are created from the given `Start` (inclusive) parameter to the given `End` (inclusive) parameter. 8 | 9 | You skip over numbers using the `Step` parameter (defaults to `1`). For example, `IntClosedRange<0, 10, 2>` will create a union of `0 | 2 | 4 | 6 | 8 | 10`. 10 | 11 | Note: `Start` or `End` must be non-negative and smaller than `999`. 12 | 13 | Use-cases: 14 | 1. This can be used to define a set of valid input/output values. for example: 15 | ``` 16 | type Age = IntClosedRange<0, 120>; //=> 0 | 1 | 2 | ... | 119 | 120 17 | type FontSize = IntClosedRange<10, 20>; //=> 10 | 11 | ... | 19 | 20 18 | type EvenNumber = IntClosedRange<0, 10, 2>; //=> 0 | 2 | 4 | 6 | 8 | 10 19 | ``` 20 | 2. This can be used to define random numbers in a range. For example, `type RandomNumber = IntClosedRange<0, 100>;` 21 | 22 | @example 23 | ``` 24 | import type {IntClosedRange} from 'type-fest'; 25 | 26 | // Create union type `0 | 1 | ... | 9` 27 | type ZeroToNine = IntClosedRange<0, 9>; 28 | 29 | // Create union type `100 | 200 | 300 | ... | 900` 30 | type Hundreds = IntClosedRange<100, 900, 100>; 31 | ``` 32 | 33 | @see IntRange 34 | */ 35 | export type IntClosedRange<Start extends number, End extends number, Skip extends number = 1> = IntRange<Start, Sum<End, 1>, Skip>; 36 | -------------------------------------------------------------------------------- /source/internal/characters.d.ts: -------------------------------------------------------------------------------- 1 | export type Whitespace = 2 | | '\u{9}' // '\t' 3 | | '\u{A}' // '\n' 4 | | '\u{B}' // '\v' 5 | | '\u{C}' // '\f' 6 | | '\u{D}' // '\r' 7 | | '\u{20}' // ' ' 8 | | '\u{85}' 9 | | '\u{A0}' 10 | | '\u{1680}' 11 | | '\u{2000}' 12 | | '\u{2001}' 13 | | '\u{2002}' 14 | | '\u{2003}' 15 | | '\u{2004}' 16 | | '\u{2005}' 17 | | '\u{2006}' 18 | | '\u{2007}' 19 | | '\u{2008}' 20 | | '\u{2009}' 21 | | '\u{200A}' 22 | | '\u{2028}' 23 | | '\u{2029}' 24 | | '\u{202F}' 25 | | '\u{205F}' 26 | | '\u{3000}' 27 | | '\u{FEFF}'; 28 | 29 | export type WordSeparators = '-' | '_' | Whitespace; 30 | 31 | export type AsciiPunctuation = 32 | | '!' 33 | | '"' 34 | | '#' 35 | | '#39; 36 | | '%' 37 | | '&' 38 | | '\'' 39 | | '(' 40 | | ')' 41 | | '*' 42 | | '+' 43 | | ',' 44 | | '-' 45 | | '.' 46 | | '/' 47 | | ':' 48 | | ';' 49 | | '<' 50 | | '=' 51 | | '>' 52 | | '?' 53 | | '@' 54 | | '[' 55 | | '\\' 56 | | ']' 57 | | '^' 58 | | '_' 59 | | '`' 60 | | '{' 61 | | '|' 62 | | '}' 63 | | '~'; 64 | -------------------------------------------------------------------------------- /source/internal/index.d.ts: -------------------------------------------------------------------------------- 1 | export type * from './array.d.ts'; 2 | export type * from './characters.d.ts'; 3 | export type * from './keys.d.ts'; 4 | export type * from './numeric.d.ts'; 5 | export type * from './object.d.ts'; 6 | export type * from './string.d.ts'; 7 | export type * from './tuple.d.ts'; 8 | export type * from './type.d.ts'; 9 | -------------------------------------------------------------------------------- /source/is-any.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | Returns a boolean for whether the given type is `any`. 3 | 4 | @link https://stackoverflow.com/a/49928360/1490091 5 | 6 | Useful in type utilities, such as disallowing `any`s to be passed to a function. 7 | 8 | @example 9 | ``` 10 | import type {IsAny} from 'type-fest'; 11 | 12 | const typedObject = {a: 1, b: 2} as const; 13 | const anyObject: any = {a: 1, b: 2}; 14 | 15 | function get<O extends (IsAny<O> extends true ? {} : Record<string, number>), K extends keyof O = keyof O>(obj: O, key: K) { 16 | return obj[key]; 17 | } 18 | 19 | const typedA = get(typedObject, 'a'); 20 | //=> 1 21 | 22 | const anyA = get(anyObject, 'a'); 23 | //=> any 24 | ``` 25 | 26 | @category Type Guard 27 | @category Utilities 28 | */ 29 | export type IsAny<T> = 0 extends 1 & NoInfer<T> ? true : false; 30 | -------------------------------------------------------------------------------- /source/is-equal.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | Returns a boolean for whether the two given types are equal. 3 | 4 | @link https://github.com/microsoft/TypeScript/issues/27024#issuecomment-421529650 5 | @link https://stackoverflow.com/questions/68961864/how-does-the-equals-work-in-typescript/68963796#68963796 6 | 7 | Use-cases: 8 | - If you want to make a conditional branch based on the result of a comparison of two types. 9 | 10 | @example 11 | ``` 12 | import type {IsEqual} from 'type-fest'; 13 | 14 | // This type returns a boolean for whether the given array includes the given item. 15 | // `IsEqual` is used to compare the given array at position 0 and the given item and then return true if they are equal. 16 | type Includes<Value extends readonly any[], Item> = 17 | Value extends readonly [Value[0], ...infer rest] 18 | ? IsEqual<Value[0], Item> extends true 19 | ? true 20 | : Includes<rest, Item> 21 | : false; 22 | ``` 23 | 24 | @category Type Guard 25 | @category Utilities 26 | */ 27 | export type IsEqual<A, B> = 28 | (<G>() => G extends A & G | G ? 1 : 2) extends 29 | (<G>() => G extends B & G | G ? 1 : 2) 30 | ? true 31 | : false; 32 | -------------------------------------------------------------------------------- /source/is-float.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | Returns a boolean for whether the given number is a float, like `1.5` or `-1.5`. 3 | 4 | Use-case: 5 | - If you want to make a conditional branch based on the result of whether a number is a float or not. 6 | 7 | @example 8 | ``` 9 | import type {IsFloat, PositiveInfinity} from 'type-fest'; 10 | 11 | type A = IsFloat<1.5>; 12 | //=> true 13 | 14 | type B = IsFloat<-1.5>; 15 | //=> true 16 | 17 | type C = IsFloat<1e-7>; 18 | //=> true 19 | 20 | type D = IsFloat<1.0>; 21 | //=> false 22 | 23 | type E = IsFloat<PositiveInfinity>; 24 | //=> false 25 | 26 | type F = IsFloat<1.23e+21>; 27 | //=> false 28 | ``` 29 | 30 | @category Type Guard 31 | @category Numeric 32 | */ 33 | export type IsFloat<T> = T extends number 34 | ? `${T}` extends `${number}e${infer E extends '-' | '+'}${number}` 35 | ? E extends '-' 36 | ? true 37 | : false 38 | : `${T}` extends `${number}.${number}` 39 | ? true 40 | : false 41 | : false; 42 | -------------------------------------------------------------------------------- /source/is-integer.d.ts: -------------------------------------------------------------------------------- 1 | import type {Not} from './internal/index.d.ts'; 2 | import type {IsFloat} from './is-float.d.ts'; 3 | import type {PositiveInfinity, NegativeInfinity} from './numeric.d.ts'; 4 | 5 | /** 6 | Returns a boolean for whether the given number is an integer, like `-5`, `1.0`, or `100`. 7 | 8 | Use-case: 9 | - If you want to make a conditional branch based on the result of whether a number is an integer or not. 10 | 11 | @example 12 | ``` 13 | import type {IsInteger, PositiveInfinity} from 'type-fest'; 14 | 15 | type A = IsInteger<1>; 16 | //=> true 17 | 18 | type B = IsInteger<1.0>; 19 | //=> true 20 | 21 | type C = IsInteger<-1>; 22 | //=> true 23 | 24 | type D = IsInteger<0b10>; 25 | //=> true 26 | 27 | type E = IsInteger<0o10>; 28 | //=> true 29 | 30 | type F = IsInteger<0x10>; 31 | //=> true 32 | 33 | type G = IsInteger<1.23+21>; 34 | //=> true 35 | 36 | type H = IsInteger<1.5>; 37 | //=> false 38 | 39 | type I = IsInteger<PositiveInfinity>; 40 | //=> false 41 | 42 | type J = IsInteger<1e-7>; 43 | //=> false 44 | ``` 45 | 46 | @category Type Guard 47 | @category Numeric 48 | */ 49 | export type IsInteger<T> = 50 | T extends bigint 51 | ? true 52 | : T extends number 53 | ? number extends T 54 | ? false 55 | : T extends PositiveInfinity | NegativeInfinity 56 | ? false 57 | : Not<IsFloat<T>> 58 | : false; 59 | -------------------------------------------------------------------------------- /source/is-lowercase.d.ts: -------------------------------------------------------------------------------- 1 | import type {AllExtend} from './all-extend.d.ts'; 2 | 3 | /** 4 | Returns a boolean for whether the given string literal is lowercase. 5 | 6 | @example 7 | ``` 8 | import type {IsLowercase} from 'type-fest'; 9 | 10 | IsLowercase<'abc'>; 11 | //=> true 12 | 13 | IsLowercase<'Abc'>; 14 | //=> false 15 | 16 | IsLowercase<string>; 17 | //=> boolean 18 | ``` 19 | */ 20 | export type IsLowercase<S extends string> = AllExtend<_IsLowercase<S>, true>; 21 | 22 | /** 23 | Loops through each part in the string and returns a boolean array indicating whether each part is lowercase. 24 | */ 25 | type _IsLowercase<S extends string, Accumulator extends boolean[] = []> = S extends `${infer First}${infer Rest}` 26 | ? _IsLowercase<Rest, [...Accumulator, IsLowercaseHelper<First>]> 27 | : [...Accumulator, IsLowercaseHelper<S>]; 28 | 29 | /** 30 | Returns a boolean for whether an individual part of the string is lowercase. 31 | */ 32 | type IsLowercaseHelper<S extends string> = S extends Lowercase<string> 33 | ? true 34 | : S extends Uppercase<string> | Capitalize<string> | `${string}${Uppercase<string>}${string}` 35 | ? false 36 | : boolean; 37 | -------------------------------------------------------------------------------- /source/is-never.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | Returns a boolean for whether the given type is `never`. 3 | 4 | @link https://github.com/microsoft/TypeScript/issues/31751#issuecomment-498526919 5 | @link https://stackoverflow.com/a/53984913/10292952 6 | @link https://www.zhenghao.io/posts/ts-never 7 | 8 | Useful in type utilities, such as checking if something does not occur. 9 | 10 | @example 11 | ``` 12 | import type {IsNever, And} from 'type-fest'; 13 | 14 | // https://github.com/andnp/SimplyTyped/blob/master/src/types/strings.ts 15 | type AreStringsEqual<A extends string, B extends string> = 16 | And< 17 | IsNever<Exclude<A, B>> extends true ? true : false, 18 | IsNever<Exclude<B, A>> extends true ? true : false 19 | >; 20 | 21 | type EndIfEqual<I extends string, O extends string> = 22 | AreStringsEqual<I, O> extends true 23 | ? never 24 | : void; 25 | 26 | function endIfEqual<I extends string, O extends string>(input: I, output: O): EndIfEqual<I, O> { 27 | if (input === output) { 28 | process.exit(0); 29 | } 30 | } 31 | 32 | endIfEqual('abc', 'abc'); 33 | //=> never 34 | 35 | endIfEqual('abc', '123'); 36 | //=> void 37 | ``` 38 | 39 | @category Type Guard 40 | @category Utilities 41 | */ 42 | export type IsNever<T> = [T] extends [never] ? true : false; 43 | -------------------------------------------------------------------------------- /source/is-null.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | Returns a boolean for whether the given type is `null`. 3 | 4 | @example 5 | ``` 6 | import type {IsNull} from 'type-fest'; 7 | 8 | type NonNullFallback<T, Fallback> = IsNull<T> extends true ? Fallback : T; 9 | 10 | type Example1 = NonNullFallback<null, string>; 11 | //=> string 12 | 13 | type Example2 = NonNullFallback<number, string>; 14 | //=? number 15 | ``` 16 | 17 | @category Type Guard 18 | @category Utilities 19 | */ 20 | export type IsNull<T> = [T] extends [null] ? true : false; 21 | -------------------------------------------------------------------------------- /source/is-nullable.d.ts: -------------------------------------------------------------------------------- 1 | import type {IsAny} from './is-any.d.ts'; 2 | 3 | /** 4 | Returns a boolean for whether the given type includes `null`. 5 | 6 | Note: The built-in `NonNullable` type removes both `null` and `undefined`, which is not accurate for the name. 7 | 8 | @example 9 | ```ts 10 | import type {IsNullable} from 'type-fest'; 11 | 12 | type A = IsNullable<string>; 13 | //=> false 14 | 15 | type B = IsNullable<string | null>; 16 | //=> true 17 | 18 | type C = IsNullable<string | undefined>; 19 | //=> false 20 | 21 | type D = IsNullable<string | null | undefined>; 22 | //=> true 23 | ``` 24 | 25 | @category Type Guard 26 | @category Utilities 27 | */ 28 | export type IsNullable<T> = IsAny<T> extends true ? true : Extract<T, null> extends never ? false : true; 29 | -------------------------------------------------------------------------------- /source/is-optional.d.ts: -------------------------------------------------------------------------------- 1 | import type {IsAny} from './is-any.d.ts'; 2 | 3 | /** 4 | Returns a boolean for whether the given type includes `undefined`. 5 | 6 | @example 7 | ```ts 8 | import type {IsOptional} from 'type-fest'; 9 | 10 | type A = IsOptional<string>; 11 | //=> false 12 | 13 | type B = IsOptional<string | undefined>; 14 | //=> true 15 | 16 | type C = IsOptional<string | null>; 17 | //=> false 18 | 19 | type D = IsOptional<string | null | undefined>; 20 | //=> true 21 | ``` 22 | 23 | @category Type Guard 24 | @category Utilities 25 | */ 26 | export type IsOptional<T> = IsAny<T> extends true ? true : Extract<T, undefined> extends never ? false : true; 27 | -------------------------------------------------------------------------------- /source/is-union.d.ts: -------------------------------------------------------------------------------- 1 | import type {IsNever} from './is-never.d.ts'; 2 | 3 | /** 4 | Returns a boolean for whether the given type is a union. 5 | 6 | @example 7 | ``` 8 | import type {IsUnion} from 'type-fest'; 9 | 10 | type A = IsUnion<string | number>; 11 | //=> true 12 | 13 | type B = IsUnion<string>; 14 | //=> false 15 | ``` 16 | */ 17 | export type IsUnion<T> = InternalIsUnion<T>; 18 | 19 | /** 20 | The actual implementation of `IsUnion`. 21 | */ 22 | type InternalIsUnion<T, U = T> = 23 | ( 24 | IsNever<T> extends true 25 | ? false 26 | : T extends any 27 | ? [U] extends [T] 28 | ? false 29 | : true 30 | : never 31 | ) extends infer Result 32 | // In some cases `Result` will return `false | true` which is `boolean`, 33 | // that means `T` has at least two types and it's a union type, 34 | // so we will return `true` instead of `boolean`. 35 | ? boolean extends Result ? true 36 | : Result 37 | : never; // Should never happen 38 | -------------------------------------------------------------------------------- /source/is-unknown.d.ts: -------------------------------------------------------------------------------- 1 | import type {IsNull} from './is-null.d.ts'; 2 | 3 | /** 4 | Returns a boolean for whether the given type is `unknown`. 5 | 6 | @link https://github.com/dsherret/conditional-type-checks/pull/16 7 | 8 | Useful in type utilities, such as when dealing with unknown data from API calls. 9 | 10 | @example 11 | ``` 12 | import type {IsUnknown} from 'type-fest'; 13 | 14 | // https://github.com/pajecawav/tiny-global-store/blob/master/src/index.ts 15 | type Action<TState, TPayload = void> = 16 | IsUnknown<TPayload> extends true 17 | ? (state: TState) => TState, 18 | : (state: TState, payload: TPayload) => TState; 19 | 20 | class Store<TState> { 21 | constructor(private state: TState) {} 22 | 23 | execute<TPayload = void>(action: Action<TState, TPayload>, payload?: TPayload): TState { 24 | this.state = action(this.state, payload); 25 | return this.state; 26 | } 27 | 28 | // ... other methods 29 | } 30 | 31 | const store = new Store({value: 1}); 32 | declare const someExternalData: unknown; 33 | 34 | store.execute(state => ({value: state.value + 1})); 35 | //=> `TPayload` is `void` 36 | 37 | store.execute((state, payload) => ({value: state.value + payload}), 5); 38 | //=> `TPayload` is `5` 39 | 40 | store.execute((state, payload) => ({value: state.value + payload}), someExternalData); 41 | //=> Errors: `action` is `(state: TState) => TState` 42 | ``` 43 | 44 | @category Utilities 45 | */ 46 | export type IsUnknown<T> = ( 47 | unknown extends T // `T` can be `unknown` or `any` 48 | ? IsNull<T> extends false // `any` can be `null`, but `unknown` can't be 49 | ? true 50 | : false 51 | : false 52 | ); 53 | -------------------------------------------------------------------------------- /source/is-uppercase.d.ts: -------------------------------------------------------------------------------- 1 | import type {AllExtend} from './all-extend.d.ts'; 2 | 3 | /** 4 | Returns a boolean for whether the given string literal is uppercase. 5 | 6 | @example 7 | ``` 8 | import type {IsUppercase} from 'type-fest'; 9 | 10 | IsUppercase<'ABC'>; 11 | //=> true 12 | 13 | IsUppercase<'Abc'>; 14 | //=> false 15 | 16 | IsUppercase<string>; 17 | //=> boolean 18 | ``` 19 | */ 20 | export type IsUppercase<S extends string> = AllExtend<_IsUppercase<S>, true>; 21 | 22 | /** 23 | Loops through each part in the string and returns a boolean array indicating whether each part is uppercase. 24 | */ 25 | type _IsUppercase<S extends string, Accumulator extends boolean[] = []> = S extends `${infer First}${infer Rest}` 26 | ? _IsUppercase<Rest, [...Accumulator, IsUppercaseHelper<First>]> 27 | : [...Accumulator, IsUppercaseHelper<S>]; 28 | 29 | /** 30 | Returns a boolean for whether an individual part of the string is uppercase. 31 | */ 32 | type IsUppercaseHelper<S extends string> = S extends Uppercase<string> 33 | ? true 34 | : S extends Lowercase<string> | Uncapitalize<string> | `${string}${Lowercase<string>}${string}` 35 | ? false 36 | : boolean; 37 | -------------------------------------------------------------------------------- /source/json-value.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | Matches a JSON object. 3 | 4 | This type can be useful to enforce some input to be JSON-compatible or as a super-type to be extended from. Don't use this as a direct return type as the user would have to double-cast it: `jsonObject as unknown as CustomResponse`. Instead, you could extend your CustomResponse type from it to ensure your type only uses JSON-compatible types: `interface CustomResponse extends JsonObject { … }`. 5 | 6 | @category JSON 7 | */ 8 | export type JsonObject = {[Key in string]: JsonValue} & {[Key in string]?: JsonValue | undefined}; 9 | 10 | /** 11 | Matches a JSON array. 12 | 13 | @category JSON 14 | */ 15 | export type JsonArray = JsonValue[] | readonly JsonValue[]; 16 | 17 | /** 18 | Matches any valid JSON primitive value. 19 | 20 | @category JSON 21 | */ 22 | export type JsonPrimitive = string | number | boolean | null; 23 | 24 | /** 25 | Matches any valid JSON value. 26 | 27 | @see `Jsonify` if you need to transform a type to one that is assignable to `JsonValue`. 28 | 29 | @category JSON 30 | */ 31 | export type JsonValue = JsonPrimitive | JsonObject | JsonArray; 32 | -------------------------------------------------------------------------------- /source/jsonifiable.d.ts: -------------------------------------------------------------------------------- 1 | import type {JsonPrimitive} from './json-value.d.ts'; 2 | 3 | type JsonifiableObject = {[Key in string]?: Jsonifiable} | {toJSON: () => Jsonifiable}; 4 | type JsonifiableArray = readonly Jsonifiable[]; 5 | 6 | /** 7 | Matches a value that can be losslessly converted to JSON. 8 | 9 | Can be used to type values that you expect to pass to `JSON.stringify`. 10 | 11 | `undefined` is allowed in object fields (for example, `{a?: number}`) as a special case even though `JSON.stringify({a: undefined})` is `{}` because it makes this class more widely useful and checking for undefined-but-present values is likely an anti-pattern. 12 | 13 | @example 14 | ``` 15 | import type {Jsonifiable} from 'type-fest'; 16 | 17 | // @ts-expect-error 18 | const error: Jsonifiable = { 19 | map: new Map([['a', 1]]), 20 | }; 21 | 22 | JSON.stringify(error); 23 | //=> {"map": {}} 24 | 25 | const good: Jsonifiable = { 26 | number: 3, 27 | date: new Date(), 28 | missing: undefined, 29 | } 30 | 31 | JSON.stringify(good); 32 | //=> {"number": 3, "date": "2022-10-17T22:22:35.920Z"} 33 | ``` 34 | 35 | @category JSON 36 | */ 37 | export type Jsonifiable = JsonPrimitive | JsonifiableObject | JsonifiableArray; 38 | -------------------------------------------------------------------------------- /source/kebab-case.d.ts: -------------------------------------------------------------------------------- 1 | import type {DefaultDelimiterCaseOptions, DelimiterCase} from './delimiter-case.d.ts'; 2 | import type {ApplyDefaultOptions} from './internal/index.d.ts'; 3 | import type {WordsOptions} from './words.d.ts'; 4 | 5 | /** 6 | Convert a string literal to kebab-case. 7 | 8 | This can be useful when, for example, converting a camel-cased object property to a kebab-cased CSS class name or a command-line flag. 9 | 10 | @example 11 | ``` 12 | import type {KebabCase} from 'type-fest'; 13 | 14 | // Simple 15 | 16 | const someVariable: KebabCase<'fooBar'> = 'foo-bar'; 17 | const someVariableNoSplitOnNumbers: KebabCase<'p2pNetwork', {splitOnNumbers: false}> = 'p2p-network'; 18 | 19 | // Advanced 20 | 21 | type KebabCasedProperties<T> = { 22 | [K in keyof T as KebabCase<K>]: T[K] 23 | }; 24 | 25 | interface CliOptions { 26 | dryRun: boolean; 27 | includeFile: string; 28 | foo: number; 29 | } 30 | 31 | const rawCliOptions: KebabCasedProperties<CliOptions> = { 32 | 'dry-run': true, 33 | 'include-file': 'bar.js', 34 | foo: 123 35 | }; 36 | ``` 37 | 38 | @category Change case 39 | @category Template literal 40 | */ 41 | export type KebabCase< 42 | Value, 43 | Options extends WordsOptions = {}, 44 | > = DelimiterCase<Value, '-', ApplyDefaultOptions<WordsOptions, DefaultDelimiterCaseOptions, Options>>; 45 | -------------------------------------------------------------------------------- /source/kebab-cased-properties-deep.d.ts: -------------------------------------------------------------------------------- 1 | import type {DefaultDelimiterCaseOptions} from './delimiter-case.d.ts'; 2 | import type {DelimiterCasedPropertiesDeep} from './delimiter-cased-properties-deep.d.ts'; 3 | import type {ApplyDefaultOptions} from './internal/index.d.ts'; 4 | import type {WordsOptions} from './words.d.ts'; 5 | 6 | /** 7 | Convert object properties to kebab case recursively. 8 | 9 | This can be useful when, for example, converting some API types from a different style. 10 | 11 | @see KebabCase 12 | @see KebabCasedProperties 13 | 14 | @example 15 | ``` 16 | import type [KebabCasedPropertiesDeep] from 'type-fest'; 17 | 18 | interface User { 19 | userId: number; 20 | userName: string; 21 | } 22 | 23 | interface UserWithFriends { 24 | userInfo: User; 25 | userFriends: User[]; 26 | } 27 | 28 | const result: KebabCasedPropertiesDeep<UserWithFriends> = { 29 | 'user-info': { 30 | 'user-id': 1, 31 | 'user-name': 'Tom', 32 | }, 33 | 'user-friends': [ 34 | { 35 | 'user-id': 2, 36 | 'user-name': 'Jerry', 37 | }, 38 | { 39 | 'user-id': 3, 40 | 'user-name': 'Spike', 41 | }, 42 | ], 43 | }; 44 | 45 | const splitOnNumbers: KebabCasedPropertiesDeep<{line1: { line2: [{ line3: string }] }}, {splitOnNumbers: true}> = { 46 | 'line-1': { 47 | 'line-2': [ 48 | { 49 | 'line-3': 'string', 50 | }, 51 | ], 52 | }, 53 | }; 54 | ``` 55 | 56 | @category Change case 57 | @category Template literal 58 | @category Object 59 | */ 60 | export type KebabCasedPropertiesDeep< 61 | Value, 62 | Options extends WordsOptions = {}, 63 | > = DelimiterCasedPropertiesDeep<Value, '-', ApplyDefaultOptions<WordsOptions, DefaultDelimiterCaseOptions, Options>>; 64 | -------------------------------------------------------------------------------- /source/kebab-cased-properties.d.ts: -------------------------------------------------------------------------------- 1 | import type {DefaultDelimiterCaseOptions} from './delimiter-case.d.ts'; 2 | import type {DelimiterCasedProperties} from './delimiter-cased-properties.d.ts'; 3 | import type {ApplyDefaultOptions} from './internal/index.d.ts'; 4 | import type {WordsOptions} from './words.d.ts'; 5 | 6 | /** 7 | Convert object properties to kebab case but not recursively. 8 | 9 | This can be useful when, for example, converting some API types from a different style. 10 | 11 | @see KebabCase 12 | @see KebabCasedPropertiesDeep 13 | 14 | @example 15 | ``` 16 | import type {KebabCasedProperties} from 'type-fest'; 17 | 18 | interface User { 19 | userId: number; 20 | userName: string; 21 | } 22 | 23 | const result: KebabCasedProperties<User> = { 24 | 'user-id': 1, 25 | 'user-name': 'Tom', 26 | }; 27 | 28 | const splitOnNumbers: KebabCasedProperties<{line1: string}, {splitOnNumbers: true}> = { 29 | 'line-1': 'string', 30 | }; 31 | ``` 32 | 33 | @category Change case 34 | @category Template literal 35 | @category Object 36 | */ 37 | export type KebabCasedProperties< 38 | Value, 39 | Options extends WordsOptions = {}, 40 | > = DelimiterCasedProperties<Value, '-', ApplyDefaultOptions<WordsOptions, DefaultDelimiterCaseOptions, Options>>; 41 | -------------------------------------------------------------------------------- /source/key-as-string.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | Get keys of the given type as strings. 3 | 4 | Number keys are converted to strings. 5 | 6 | Use-cases: 7 | - Get string keys from a type which may have number keys. 8 | - Makes it possible to index using strings retrieved from template types. 9 | 10 | @example 11 | ``` 12 | import type {KeyAsString} from 'type-fest'; 13 | 14 | type Foo = { 15 | 1: number; 16 | stringKey: string; 17 | }; 18 | 19 | type StringKeysOfFoo = KeyAsString<Foo>; 20 | //=> '1' | 'stringKey' 21 | ``` 22 | 23 | @category Object 24 | */ 25 | export type KeyAsString<BaseType> = `${Extract<keyof BaseType, string | number>}`; 26 | -------------------------------------------------------------------------------- /source/keys-of-union.d.ts: -------------------------------------------------------------------------------- 1 | import type {UnionToIntersection} from './union-to-intersection.d.ts'; 2 | 3 | /** 4 | Create a union of all keys from a given type, even those exclusive to specific union members. 5 | 6 | Unlike the native `keyof` keyword, which returns keys present in **all** union members, this type returns keys from **any** member. 7 | 8 | @link https://stackoverflow.com/a/49402091 9 | 10 | @example 11 | ``` 12 | import type {KeysOfUnion} from 'type-fest'; 13 | 14 | type A = { 15 | common: string; 16 | a: number; 17 | }; 18 | 19 | type B = { 20 | common: string; 21 | b: string; 22 | }; 23 | 24 | type C = { 25 | common: string; 26 | c: boolean; 27 | }; 28 | 29 | type Union = A | B | C; 30 | 31 | type CommonKeys = keyof Union; 32 | //=> 'common' 33 | 34 | type AllKeys = KeysOfUnion<Union>; 35 | //=> 'common' | 'a' | 'b' | 'c' 36 | ``` 37 | 38 | @category Object 39 | */ 40 | export type KeysOfUnion<ObjectType> = 41 | // Hack to fix https://github.com/sindresorhus/type-fest/issues/1008 42 | keyof UnionToIntersection<ObjectType extends unknown ? Record<keyof ObjectType, never> : never>; 43 | -------------------------------------------------------------------------------- /source/last-array-element.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | Extracts the type of the last element of an array. 3 | 4 | Use-case: Defining the return type of functions that extract the last element of an array, for example [`lodash.last`](https://lodash.com/docs/4.17.15#last). 5 | 6 | @example 7 | ``` 8 | import type {LastArrayElement} from 'type-fest'; 9 | 10 | declare function lastOf<V extends readonly any[]>(array: V): LastArrayElement<V>; 11 | 12 | const array = ['foo', 2]; 13 | 14 | typeof lastOf(array); 15 | //=> number 16 | 17 | const array = ['foo', 2] as const; 18 | 19 | typeof lastOf(array); 20 | //=> 2 21 | ``` 22 | 23 | @category Array 24 | @category Template literal 25 | */ 26 | export type LastArrayElement<Elements extends readonly unknown[], ElementBeforeTailingSpreadElement = never> = 27 | // If the last element of an array is a spread element, the `LastArrayElement` result should be `'the type of the element before the spread element' | 'the type of the spread element'`. 28 | Elements extends readonly [] 29 | ? ElementBeforeTailingSpreadElement 30 | : Elements extends readonly [...infer U, infer V] 31 | ? V 32 | : Elements extends readonly [infer U, ...infer V] 33 | // If we return `V[number] | U` directly, it would be wrong for `[[string, boolean, object, ...number[]]`. 34 | // So we need to recurse type `V` and carry over the type of the element before the spread element. 35 | ? LastArrayElement<V, U> 36 | : Elements extends ReadonlyArray<infer U> 37 | ? U | ElementBeforeTailingSpreadElement 38 | : never; 39 | -------------------------------------------------------------------------------- /source/less-than-or-equal.d.ts: -------------------------------------------------------------------------------- 1 | import type {GreaterThan} from './greater-than.d.ts'; 2 | 3 | /** 4 | Returns a boolean for whether a given number is less than or equal to another number. 5 | 6 | @example 7 | ``` 8 | import type {LessThanOrEqual} from 'type-fest'; 9 | 10 | LessThanOrEqual<1, -5>; 11 | //=> false 12 | 13 | LessThanOrEqual<1, 1>; 14 | //=> true 15 | 16 | LessThanOrEqual<1, 5>; 17 | //=> true 18 | ``` 19 | */ 20 | export type LessThanOrEqual<A extends number, B extends number> = number extends A | B 21 | ? never 22 | : GreaterThan<A, B> extends true ? false : true; 23 | -------------------------------------------------------------------------------- /source/less-than.d.ts: -------------------------------------------------------------------------------- 1 | import type {GreaterThanOrEqual} from './greater-than-or-equal.d.ts'; 2 | 3 | /** 4 | Returns a boolean for whether a given number is less than another number. 5 | 6 | @example 7 | ``` 8 | import type {LessThan} from 'type-fest'; 9 | 10 | LessThan<1, -5>; 11 | //=> false 12 | 13 | LessThan<1, 1>; 14 | //=> false 15 | 16 | LessThan<1, 5>; 17 | //=> true 18 | ``` 19 | */ 20 | export type LessThan<A extends number, B extends number> = number extends A | B 21 | ? never 22 | : GreaterThanOrEqual<A, B> extends infer Result 23 | ? Result extends true 24 | ? false 25 | : true 26 | : never; // Should never happen 27 | -------------------------------------------------------------------------------- /source/literal-to-primitive-deep.d.ts: -------------------------------------------------------------------------------- 1 | import type {LiteralToPrimitive} from './literal-to-primitive.d.ts'; 2 | import type {OmitIndexSignature} from './omit-index-signature.d.ts'; 3 | 4 | /** 5 | Like `LiteralToPrimitive` except it converts literal types inside an object or array deeply. 6 | 7 | For example, given a constant object, it returns a new object type with the same keys but with all the values converted to primitives. 8 | 9 | @see LiteralToPrimitive 10 | 11 | Use-case: Deal with data that is imported from a JSON file. 12 | 13 | @example 14 | ``` 15 | import type {LiteralToPrimitiveDeep, TsConfigJson} from 'type-fest'; 16 | import tsconfig from 'path/to/tsconfig.json'; 17 | 18 | function doSomethingWithTSConfig(config: LiteralToPrimitiveDeep<TsConfigJson>) { ... } 19 | 20 | // No casting is needed to pass the type check 21 | doSomethingWithTSConfig(tsconfig); 22 | 23 | // If LiteralToPrimitiveDeep is not used, you need to cast the imported data like this: 24 | doSomethingWithTSConfig(tsconfig as TsConfigJson); 25 | ``` 26 | 27 | @category Type 28 | @category Object 29 | */ 30 | export type LiteralToPrimitiveDeep<T> = T extends object 31 | ? T extends Array<infer U> 32 | ? Array<LiteralToPrimitiveDeep<U>> 33 | : { 34 | [K in keyof OmitIndexSignature<T>]: LiteralToPrimitiveDeep<T[K]>; 35 | } 36 | : LiteralToPrimitive<T>; 37 | -------------------------------------------------------------------------------- /source/literal-to-primitive.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | Given a [literal type](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#literal-types) return the {@link Primitive | primitive type} it belongs to, or `never` if it's not a primitive. 3 | 4 | Use-case: Working with generic types that may be literal types. 5 | 6 | @example 7 | ``` 8 | import type {LiteralToPrimitive} from 'type-fest'; 9 | 10 | // No overloads needed to get the correct return type 11 | function plus<T extends number | bigint | string>(x: T, y: T): LiteralToPrimitive<T> { 12 | return x + (y as any); 13 | } 14 | 15 | plus('a', 'b'); // string 16 | plus(1, 2); // number 17 | plus(1n, 2n); // bigint 18 | ``` 19 | 20 | @category Type 21 | */ 22 | export type LiteralToPrimitive<T> = T extends number 23 | ? number 24 | : T extends bigint 25 | ? bigint 26 | : T extends string 27 | ? string 28 | : T extends boolean 29 | ? boolean 30 | : T extends symbol 31 | ? symbol 32 | : T extends null 33 | ? null 34 | : T extends undefined 35 | ? undefined 36 | : never; 37 | -------------------------------------------------------------------------------- /source/literal-union.d.ts: -------------------------------------------------------------------------------- 1 | import type {Primitive} from './primitive.d.ts'; 2 | 3 | export type LiteralStringUnion<T> = LiteralUnion<T, string>; 4 | 5 | /** 6 | Allows creating a union type by combining primitive types and literal types without sacrificing auto-completion in IDEs for the literal type part of the union. 7 | 8 | Currently, when a union type of a primitive type is combined with literal types, TypeScript loses all information about the combined literals. Thus, when such type is used in an IDE with autocompletion, no suggestions are made for the declared literals. 9 | 10 | This type is a workaround for [Microsoft/TypeScript#29729](https://github.com/Microsoft/TypeScript/issues/29729). It will be removed as soon as it's not needed anymore. 11 | 12 | @example 13 | ``` 14 | import type {LiteralUnion} from 'type-fest'; 15 | 16 | // Before 17 | 18 | type Pet = 'dog' | 'cat' | string; 19 | 20 | const pet: Pet = ''; 21 | // Start typing in your TypeScript-enabled IDE. 22 | // You **will not** get auto-completion for `dog` and `cat` literals. 23 | 24 | // After 25 | 26 | type Pet2 = LiteralUnion<'dog' | 'cat', string>; 27 | 28 | const pet: Pet2 = ''; 29 | // You **will** get auto-completion for `dog` and `cat` literals. 30 | ``` 31 | 32 | @category Type 33 | */ 34 | export type LiteralUnion< 35 | LiteralType, 36 | BaseType extends Primitive, 37 | > = LiteralType | (BaseType & Record<never, never>); 38 | -------------------------------------------------------------------------------- /source/merge-exclusive.d.ts: -------------------------------------------------------------------------------- 1 | // Helper type. Not useful on its own. 2 | type Without<FirstType, SecondType> = {[KeyType in Exclude<keyof FirstType, keyof SecondType>]?: never}; 3 | 4 | /** 5 | Create a type that has mutually exclusive keys. 6 | 7 | This type was inspired by [this comment](https://github.com/Microsoft/TypeScript/issues/14094#issuecomment-373782604). 8 | 9 | This type works with a helper type, called `Without`. `Without<FirstType, SecondType>` produces a type that has only keys from `FirstType` which are not present on `SecondType` and sets the value type for these keys to `never`. This helper type is then used in `MergeExclusive` to remove keys from either `FirstType` or `SecondType`. 10 | 11 | @example 12 | ``` 13 | import type {MergeExclusive} from 'type-fest'; 14 | 15 | interface ExclusiveVariation1 { 16 | exclusive1: boolean; 17 | } 18 | 19 | interface ExclusiveVariation2 { 20 | exclusive2: string; 21 | } 22 | 23 | type ExclusiveOptions = MergeExclusive<ExclusiveVariation1, ExclusiveVariation2>; 24 | 25 | let exclusiveOptions: ExclusiveOptions; 26 | 27 | exclusiveOptions = {exclusive1: true}; 28 | //=> Works 29 | exclusiveOptions = {exclusive2: 'hi'}; 30 | //=> Works 31 | exclusiveOptions = {exclusive1: true, exclusive2: 'hi'}; 32 | //=> Error 33 | ``` 34 | 35 | @category Object 36 | */ 37 | export type MergeExclusive<FirstType, SecondType> = 38 | (FirstType | SecondType) extends object ? 39 | (Without<FirstType, SecondType> & SecondType) | (Without<SecondType, FirstType> & FirstType) : 40 | FirstType | SecondType; 41 | 42 | -------------------------------------------------------------------------------- /source/merge.d.ts: -------------------------------------------------------------------------------- 1 | import type {OmitIndexSignature} from './omit-index-signature.d.ts'; 2 | import type {PickIndexSignature} from './pick-index-signature.d.ts'; 3 | import type {Simplify} from './simplify.d.ts'; 4 | 5 | // Merges two objects without worrying about index signatures. 6 | type SimpleMerge<Destination, Source> = { 7 | [Key in keyof Destination as Key extends keyof Source ? never : Key]: Destination[Key]; 8 | } & Source; 9 | 10 | /** 11 | Merge two types into a new type. Keys of the second type overrides keys of the first type. 12 | 13 | @example 14 | ``` 15 | import type {Merge} from 'type-fest'; 16 | 17 | interface Foo { 18 | [x: string]: unknown; 19 | [x: number]: unknown; 20 | foo: string; 21 | bar: symbol; 22 | } 23 | 24 | type Bar = { 25 | [x: number]: number; 26 | [x: symbol]: unknown; 27 | bar: Date; 28 | baz: boolean; 29 | }; 30 | 31 | export type FooBar = Merge<Foo, Bar>; 32 | // => { 33 | // [x: string]: unknown; 34 | // [x: number]: number; 35 | // [x: symbol]: unknown; 36 | // foo: string; 37 | // bar: Date; 38 | // baz: boolean; 39 | // } 40 | ``` 41 | 42 | @category Object 43 | */ 44 | export type Merge<Destination, Source> = 45 | Simplify< 46 | SimpleMerge<PickIndexSignature<Destination>, PickIndexSignature<Source>> 47 | & SimpleMerge<OmitIndexSignature<Destination>, OmitIndexSignature<Source>> 48 | >; 49 | -------------------------------------------------------------------------------- /source/multidimensional-array.d.ts: -------------------------------------------------------------------------------- 1 | import type {Subtract} from './subtract.d.ts'; 2 | import type {IsEqual} from './is-equal.d.ts'; 3 | 4 | type Recursive<T> = Array<Recursive<T>>; 5 | 6 | /** 7 | Creates a type that represents a multidimensional array of the given type and dimension. 8 | 9 | Use-cases: 10 | - Return a n-dimensional array from functions. 11 | - Declare a n-dimensional array by defining its dimensions rather than declaring `[]` repetitively. 12 | - Infer the dimensions of a n-dimensional array automatically from function arguments. 13 | - Avoid the need to know in advance the dimensions of a n-dimensional array allowing them to be dynamic. 14 | 15 | @example 16 | ``` 17 | import type {MultidimensionalArray} from 'type-fest'; 18 | 19 | function emptyMatrix<T extends number>(dimensions: T): MultidimensionalArray<unknown, T> { 20 | const matrix: unknown[] = []; 21 | 22 | let subMatrix = matrix; 23 | for (let dimension = 1; dimension < dimensions; ++dimension) { 24 | console.log(`Initializing dimension #${dimension}`); 25 | 26 | subMatrix[0] = []; 27 | subMatrix = subMatrix[0] as unknown[]; 28 | } 29 | 30 | return matrix as MultidimensionalArray<unknown, T>; 31 | } 32 | 33 | const matrix = emptyMatrix(3); 34 | 35 | matrix[0][0][0] = 42; 36 | ``` 37 | 38 | @category Array 39 | */ 40 | export type MultidimensionalArray<Element, Dimensions extends number> = number extends Dimensions 41 | ? Recursive<Element> 42 | : IsEqual<Dimensions, 0> extends true 43 | ? Element 44 | : Array<MultidimensionalArray<Element, Subtract<Dimensions, 1>>>; 45 | -------------------------------------------------------------------------------- /source/multidimensional-readonly-array.d.ts: -------------------------------------------------------------------------------- 1 | import type {Subtract} from './subtract.d.ts'; 2 | import type {IsEqual} from './is-equal.d.ts'; 3 | 4 | type Recursive<T> = ReadonlyArray<Recursive<T>>; 5 | 6 | /** 7 | Creates a type that represents a multidimensional readonly array that of the given type and dimension. 8 | 9 | Use-cases: 10 | - Return a n-dimensional array from functions. 11 | - Declare a n-dimensional array by defining its dimensions rather than declaring `[]` repetitively. 12 | - Infer the dimensions of a n-dimensional array automatically from function arguments. 13 | - Avoid the need to know in advance the dimensions of a n-dimensional array allowing them to be dynamic. 14 | 15 | @example 16 | ``` 17 | import type {MultidimensionalReadonlyArray} from 'type-fest'; 18 | 19 | function emptyMatrix<T extends number>(dimensions: T): MultidimensionalReadonlyArray<unknown, T> { 20 | const matrix: unknown[] = []; 21 | 22 | let subMatrix = matrix; 23 | for (let dimension = 1; dimension < dimensions; ++dimension) { 24 | console.log(`Initializing dimension #${dimension}`); 25 | 26 | subMatrix[0] = []; 27 | if (dimension < dimensions - 1) { 28 | subMatrix = subMatrix[0] as unknown[]; 29 | } else { 30 | subMatrix[0] = 42; 31 | } 32 | } 33 | 34 | return matrix as MultidimensionalReadonlyArray<unknown, T>; 35 | } 36 | 37 | const matrix = emptyMatrix(3); 38 | 39 | const answer = matrix[0][0][0]; // 42 40 | ``` 41 | 42 | @category Array 43 | */ 44 | export type MultidimensionalReadonlyArray<Element, Dimensions extends number> = number extends Dimensions 45 | ? Recursive<Element> 46 | : IsEqual<Dimensions, 0> extends true 47 | ? Element 48 | : ReadonlyArray<MultidimensionalReadonlyArray<Element, Subtract<Dimensions, 1>>>; 49 | -------------------------------------------------------------------------------- /source/non-empty-object.d.ts: -------------------------------------------------------------------------------- 1 | import type {HasRequiredKeys} from './has-required-keys.d.ts'; 2 | import type {RequireAtLeastOne} from './require-at-least-one.d.ts'; 3 | 4 | /** 5 | Represents an object with at least 1 non-optional key. 6 | 7 | This is useful when you need an object where all keys are optional, but there must be at least 1 key. 8 | 9 | @example 10 | ``` 11 | import type {NonEmptyObject} from 'type-fest'; 12 | 13 | type User = { 14 | name: string; 15 | surname: string; 16 | id: number; 17 | }; 18 | 19 | type UpdateRequest<Entity extends object> = NonEmptyObject<Partial<Entity>>; 20 | 21 | const update1: UpdateRequest<User> = { 22 | name: 'Alice', 23 | surname: 'Acme', 24 | }; 25 | 26 | // At least 1 key is required, therefore this will report a 2322 error: 27 | // Type '{}' is not assignable to type 'UpdateRequest<User>' 28 | const update2: UpdateRequest<User> = {}; 29 | ``` 30 | 31 | @see Use `IsEmptyObject` to check whether an object is empty. 32 | 33 | @category Object 34 | */ 35 | export type NonEmptyObject<T extends object> = HasRequiredKeys<T> extends true ? T : RequireAtLeastOne<T, keyof T>; 36 | -------------------------------------------------------------------------------- /source/non-empty-string.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | Matches any non-empty string. 3 | 4 | This is useful when you need a string that is not empty, for example, as a function parameter. 5 | 6 | NOTE: 7 | - This returns `never` not just when instantiated with an empty string, but also when an empty string is a subtype of the instantiated type, like `string` or `Uppercase<string>`. 8 | 9 | @example 10 | ``` 11 | import type {NonEmptyString} from 'type-fest'; 12 | 13 | declare function foo<T extends string>(string: NonEmptyString<T>): void; 14 | 15 | foo('a'); 16 | //=> OK 17 | 18 | foo(''); 19 | //=> Error: Argument of type '""' is not assignable to parameter of type 'never'. 20 | 21 | declare const someString: string 22 | foo(someString); 23 | //=> Error: Argument of type 'string' is not assignable to parameter of type 'never'. 24 | ``` 25 | 26 | @category String 27 | */ 28 | export type NonEmptyString<T extends string> = '' extends T ? never : T; 29 | -------------------------------------------------------------------------------- /source/non-empty-tuple.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | Matches any non-empty tuple. 3 | 4 | @example 5 | ``` 6 | import type {NonEmptyTuple} from 'type-fest'; 7 | 8 | const sum = (...numbers: NonEmptyTuple<number>) => numbers.reduce((total, value) => total + value, 0); 9 | 10 | sum(1, 2, 3); 11 | //=> 6 12 | 13 | sum(); 14 | //=> Error: Expected at least 1 arguments, but got 0. 15 | ``` 16 | 17 | @see {@link RequireAtLeastOne} for objects 18 | 19 | @category Array 20 | */ 21 | export type NonEmptyTuple<T = unknown> = readonly [T, ...T[]]; 22 | -------------------------------------------------------------------------------- /source/opaque.d.ts: -------------------------------------------------------------------------------- 1 | export type * from './tagged.d.ts'; 2 | -------------------------------------------------------------------------------- /source/optional-keys-of.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | Extract all optional keys from the given type. 3 | 4 | This is useful when you want to create a new type that contains different type values for the optional keys only. 5 | 6 | @example 7 | ``` 8 | import type {OptionalKeysOf, Except} from 'type-fest'; 9 | 10 | interface User { 11 | name: string; 12 | surname: string; 13 | 14 | luckyNumber?: number; 15 | } 16 | 17 | const REMOVE_FIELD = Symbol('remove field symbol'); 18 | type UpdateOperation<Entity extends object> = Except<Partial<Entity>, OptionalKeysOf<Entity>> & { 19 | [Key in OptionalKeysOf<Entity>]?: Entity[Key] | typeof REMOVE_FIELD; 20 | }; 21 | 22 | const update1: UpdateOperation<User> = { 23 | name: 'Alice' 24 | }; 25 | 26 | const update2: UpdateOperation<User> = { 27 | name: 'Bob', 28 | luckyNumber: REMOVE_FIELD 29 | }; 30 | ``` 31 | 32 | @category Utilities 33 | */ 34 | export type OptionalKeysOf<BaseType extends object> = 35 | BaseType extends unknown // For distributing `BaseType` 36 | ? (keyof { 37 | [Key in keyof BaseType as BaseType extends Record<Key, BaseType[Key]> ? never : Key]: never 38 | }) & (keyof BaseType) // Intersect with `keyof BaseType` to ensure result of `OptionalKeysOf<BaseType>` is always assignable to `keyof BaseType` 39 | : never; // Should never happen 40 | -------------------------------------------------------------------------------- /source/override-properties.d.ts: -------------------------------------------------------------------------------- 1 | import type {Merge} from './merge.d.ts'; 2 | 3 | /** 4 | Override existing properties of the given type. Similar to `Merge`, but enforces that the original type has the properties you want to override. 5 | 6 | This is useful when you want to override existing properties with a different type and make sure that these properties really exist in the original. 7 | 8 | @example 9 | ``` 10 | type Foo = { 11 | a: string 12 | b: string 13 | } 14 | type Bar = OverrideProperties<Foo, {b: number}> 15 | //=> {a: string, b: number} 16 | 17 | type Baz = OverrideProperties<Foo, {c: number}> 18 | // Error, type '{ c: number; }' does not satisfy the constraint '{ c: never; }' 19 | 20 | type Fizz = OverrideProperties<Foo, {b: number; c: number}> 21 | // Error, type '{ b: number; c: number; }' does not satisfy the constraint '{ b: number; c: never; }' 22 | ``` 23 | 24 | @category Object 25 | */ 26 | export type OverrideProperties< 27 | TOriginal, 28 | // This first bit where we use `Partial` is to enable autocomplete 29 | // and the second bit with the mapped type is what enforces that we don't try 30 | // to override properties that doesn't exist in the original type. 31 | TOverride extends Partial<Record<keyof TOriginal, unknown>> & { 32 | [Key in keyof TOverride]: Key extends keyof TOriginal 33 | ? TOverride[Key] 34 | : never; 35 | }, 36 | > = Merge<TOriginal, TOverride>; 37 | -------------------------------------------------------------------------------- /source/pascal-case.d.ts: -------------------------------------------------------------------------------- 1 | import type {CamelCase, CamelCaseOptions, DefaultCamelCaseOptions} from './camel-case.d.ts'; 2 | import type {ApplyDefaultOptions} from './internal/index.d.ts'; 3 | 4 | /** 5 | Converts a string literal to pascal-case. 6 | 7 | @example 8 | ``` 9 | import type {PascalCase} from 'type-fest'; 10 | 11 | // Simple 12 | 13 | const someVariable: PascalCase<'foo-bar'> = 'FooBar'; 14 | const preserveConsecutiveUppercase: PascalCase<'foo-BAR-baz', {preserveConsecutiveUppercase: true}> = 'FooBARBaz'; 15 | 16 | // Advanced 17 | 18 | type PascalCasedProperties<T> = { 19 | [K in keyof T as PascalCase<K>]: T[K] 20 | }; 21 | 22 | interface RawOptions { 23 | 'dry-run': boolean; 24 | 'full_family_name': string; 25 | foo: number; 26 | BAR: string; 27 | QUZ_QUX: number; 28 | 'OTHER-FIELD': boolean; 29 | }; 30 | 31 | const dbResult: PascalCasedProperties<RawOptions> = { 32 | DryRun: true, 33 | FullFamilyName: 'bar.js', 34 | Foo: 123, 35 | Bar: 'foo', 36 | QuzQux: 6, 37 | OtherField: false, 38 | }; 39 | ``` 40 | 41 | @category Change case 42 | @category Template literal 43 | */ 44 | export type PascalCase<Value, Options extends CamelCaseOptions = {}> = 45 | _PascalCase<Value, ApplyDefaultOptions<CamelCaseOptions, DefaultCamelCaseOptions, Options>>; 46 | 47 | type _PascalCase<Value, Options extends Required<CamelCaseOptions>> = CamelCase<Value, Options> extends string 48 | ? Capitalize<CamelCase<Value, Options>> 49 | : CamelCase<Value, Options>; 50 | -------------------------------------------------------------------------------- /source/pascal-cased-properties.d.ts: -------------------------------------------------------------------------------- 1 | import type {CamelCaseOptions, DefaultCamelCaseOptions} from './camel-case.d.ts'; 2 | import type {ApplyDefaultOptions} from './internal/index.d.ts'; 3 | import type {PascalCase} from './pascal-case.d.ts'; 4 | 5 | /** 6 | Convert object properties to pascal case but not recursively. 7 | 8 | This can be useful when, for example, converting some API types from a different style. 9 | 10 | @see PascalCase 11 | @see PascalCasedPropertiesDeep 12 | 13 | @example 14 | ``` 15 | import type {PascalCasedProperties} from 'type-fest'; 16 | 17 | interface User { 18 | userId: number; 19 | userName: string; 20 | } 21 | 22 | const result: PascalCasedProperties<User> = { 23 | UserId: 1, 24 | UserName: 'Tom', 25 | }; 26 | 27 | const preserveConsecutiveUppercase: PascalCasedProperties<{fooBAR: string}, {preserveConsecutiveUppercase: true}> = { 28 | FooBAR: 'string', 29 | }; 30 | ``` 31 | 32 | @category Change case 33 | @category Template literal 34 | @category Object 35 | */ 36 | export type PascalCasedProperties<Value, Options extends CamelCaseOptions = {}> = Value extends Function 37 | ? Value 38 | : Value extends Array<infer U> 39 | ? Value 40 | : {[K in keyof Value as PascalCase<K, ApplyDefaultOptions<CamelCaseOptions, DefaultCamelCaseOptions, Options>>]: Value[K]}; 41 | -------------------------------------------------------------------------------- /source/pick-index-signature.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | Pick only index signatures from the given object type, leaving out all explicitly defined properties. 3 | 4 | This is the counterpart of `OmitIndexSignature`. 5 | 6 | @example 7 | ``` 8 | import type {PickIndexSignature} from 'type-fest'; 9 | 10 | declare const symbolKey: unique symbol; 11 | 12 | type Example = { 13 | // These index signatures will remain. 14 | [x: string]: unknown; 15 | [x: number]: unknown; 16 | [x: symbol]: unknown; 17 | [x: `head-${string}`]: string; 18 | [x: `${string}-tail`]: string; 19 | [x: `head-${string}-tail`]: string; 20 | [x: `${bigint}`]: string; 21 | [x: `embedded-${number}`]: string; 22 | 23 | // These explicitly defined keys will be removed. 24 | ['kebab-case-key']: string; 25 | [symbolKey]: string; 26 | foo: 'bar'; 27 | qux?: 'baz'; 28 | }; 29 | 30 | type ExampleIndexSignature = PickIndexSignature<Example>; 31 | // { 32 | // [x: string]: unknown; 33 | // [x: number]: unknown; 34 | // [x: symbol]: unknown; 35 | // [x: `head-${string}`]: string; 36 | // [x: `${string}-tail`]: string; 37 | // [x: `head-${string}-tail`]: string; 38 | // [x: `${bigint}`]: string; 39 | // [x: `embedded-${number}`]: string; 40 | // } 41 | ``` 42 | 43 | @see OmitIndexSignature 44 | @category Object 45 | */ 46 | export type PickIndexSignature<ObjectType> = { 47 | [KeyType in keyof ObjectType as {} extends Record<KeyType, unknown> 48 | ? KeyType 49 | : never]: ObjectType[KeyType]; 50 | }; 51 | -------------------------------------------------------------------------------- /source/primitive.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | Matches any [primitive value](https://developer.mozilla.org/en-US/docs/Glossary/Primitive). 3 | 4 | @category Type 5 | */ 6 | export type Primitive = 7 | | null 8 | | undefined 9 | | string 10 | | number 11 | | boolean 12 | | symbol 13 | | bigint; 14 | -------------------------------------------------------------------------------- /source/promisable.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | Create a type that represents either the value or the value wrapped in `PromiseLike`. 3 | 4 | Use-cases: 5 | - A function accepts a callback that may either return a value synchronously or may return a promised value. 6 | - This type could be the return type of `Promise#then()`, `Promise#catch()`, and `Promise#finally()` callbacks. 7 | 8 | Please upvote [this issue](https://github.com/microsoft/TypeScript/issues/31394) if you want to have this type as a built-in in TypeScript. 9 | 10 | @example 11 | ``` 12 | import type {Promisable} from 'type-fest'; 13 | 14 | async function logger(getLogEntry: () => Promisable<string>): Promise<void> { 15 | const entry = await getLogEntry(); 16 | console.log(entry); 17 | } 18 | 19 | logger(() => 'foo'); 20 | logger(() => Promise.resolve('bar')); 21 | ``` 22 | 23 | @category Async 24 | */ 25 | export type Promisable<T> = T | PromiseLike<T>; 26 | -------------------------------------------------------------------------------- /source/readonly-keys-of.d.ts: -------------------------------------------------------------------------------- 1 | import type {WritableKeysOf} from './writable-keys-of.d.ts'; 2 | 3 | /** 4 | Extract all readonly keys from the given type. 5 | 6 | This is useful when you want to create a new type that contains readonly keys only. 7 | 8 | @example 9 | ``` 10 | import type {ReadonlyKeysOf} from 'type-fest'; 11 | 12 | interface User { 13 | name: string; 14 | surname: string; 15 | readonly id: number; 16 | } 17 | 18 | type UpdateResponse<Entity extends object> = Pick<Entity, ReadonlyKeysOf<Entity>>; 19 | 20 | const update1: UpdateResponse<User> = { 21 | id: 123, 22 | }; 23 | ``` 24 | 25 | @category Utilities 26 | */ 27 | export type ReadonlyKeysOf<T extends object> = 28 | T extends unknown // For distributing `T` 29 | ? Exclude<keyof T, WritableKeysOf<T>> 30 | : never; // Should never happen 31 | -------------------------------------------------------------------------------- /source/readonly-tuple.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | Creates a read-only tuple of type `Element` and with the length of `Length`. 3 | 4 | @private 5 | @see `ReadonlyTuple` which is safer because it tests if `Length` is a specific finite number. 6 | */ 7 | type BuildTupleHelper<Element, Length extends number, Rest extends Element[]> = 8 | Rest['length'] extends Length ? 9 | readonly [...Rest] : // Terminate with readonly array (aka tuple) 10 | BuildTupleHelper<Element, Length, [Element, ...Rest]>; 11 | 12 | /** 13 | Create a type that represents a read-only tuple of the given type and length. 14 | 15 | Use-cases: 16 | - Declaring fixed-length tuples with a large number of items. 17 | - Creating a range union (for example, `0 | 1 | 2 | 3 | 4` from the keys of such a type) without having to resort to recursive types. 18 | - Creating a tuple of coordinates with a static length, for example, length of 3 for a 3D vector. 19 | 20 | @example 21 | ``` 22 | import {ReadonlyTuple} from 'type-fest'; 23 | 24 | type FencingTeam = ReadonlyTuple<string, 3>; 25 | 26 | const guestFencingTeam: FencingTeam = ['Josh', 'Michael', 'Robert']; 27 | 28 | const homeFencingTeam: FencingTeam = ['George', 'John']; 29 | //=> error TS2322: Type string[] is not assignable to type 'FencingTeam' 30 | 31 | guestFencingTeam.push('Sam'); 32 | //=> error TS2339: Property 'push' does not exist on type 'FencingTeam' 33 | ``` 34 | 35 | @category Utilities 36 | */ 37 | export type ReadonlyTuple<Element, Length extends number> = 38 | number extends Length 39 | // Because `Length extends number` and `number extends Length`, then `Length` is not a specific finite number. 40 | ? readonly Element[] // It's not fixed length. 41 | : BuildTupleHelper<Element, Length, []>; // Otherwise it is a fixed length tuple. 42 | -------------------------------------------------------------------------------- /source/require-at-least-one.d.ts: -------------------------------------------------------------------------------- 1 | import type {Except} from './except.d.ts'; 2 | import type {If} from './if.d.ts'; 3 | import type {IfNotAnyOrNever} from './internal/index.d.ts'; 4 | import type {IsAny} from './is-any.d.ts'; 5 | import type {IsNever} from './is-never.d.ts'; 6 | 7 | /** 8 | Create a type that requires at least one of the given keys. The remaining keys are kept as is. 9 | 10 | @example 11 | ``` 12 | import type {RequireAtLeastOne} from 'type-fest'; 13 | 14 | type Responder = { 15 | text?: () => string; 16 | json?: () => string; 17 | secure?: boolean; 18 | }; 19 | 20 | const responder: RequireAtLeastOne<Responder, 'text' | 'json'> = { 21 | json: () => '{"message": "ok"}', 22 | secure: true 23 | }; 24 | ``` 25 | 26 | @category Object 27 | */ 28 | export type RequireAtLeastOne< 29 | ObjectType, 30 | KeysType extends keyof ObjectType = keyof ObjectType, 31 | > = 32 | IfNotAnyOrNever<ObjectType, 33 | If<IsNever<KeysType>, 34 | never, 35 | _RequireAtLeastOne<ObjectType, If<IsAny<KeysType>, keyof ObjectType, KeysType>> 36 | >>; 37 | 38 | type _RequireAtLeastOne< 39 | ObjectType, 40 | KeysType extends keyof ObjectType, 41 | > = { 42 | // For each `Key` in `KeysType` make a mapped type: 43 | [Key in KeysType]-?: Required<Pick<ObjectType, Key>> & // 1. Make `Key`'s type required 44 | // 2. Make all other keys in `KeysType` optional 45 | Partial<Pick<ObjectType, Exclude<KeysType, Key>>>; 46 | }[KeysType] & 47 | // 3. Add the remaining keys not in `KeysType` 48 | Except<ObjectType, KeysType>; 49 | -------------------------------------------------------------------------------- /source/require-one-or-none.d.ts: -------------------------------------------------------------------------------- 1 | import type {RequireExactlyOne} from './require-exactly-one.d.ts'; 2 | import type {IfNotAnyOrNever, RequireNone} from './internal/index.d.ts'; 3 | import type {If} from './if.d.ts'; 4 | import type {IsAny} from './is-any.d.ts'; 5 | import type {IsNever} from './is-never.d.ts'; 6 | 7 | /** 8 | Create a type that requires exactly one of the given keys and disallows more, or none of the given keys. The remaining keys are kept as is. 9 | 10 | @example 11 | ``` 12 | import type {RequireOneOrNone} from 'type-fest'; 13 | 14 | type Responder = RequireOneOrNone<{ 15 | text: () => string; 16 | json: () => string; 17 | secure: boolean; 18 | }, 'text' | 'json'>; 19 | 20 | const responder1: Responder = { 21 | secure: true 22 | }; 23 | 24 | const responder2: Responder = { 25 | text: () => '{"message": "hi"}', 26 | secure: true 27 | }; 28 | 29 | const responder3: Responder = { 30 | json: () => '{"message": "ok"}', 31 | secure: true 32 | }; 33 | ``` 34 | 35 | @category Object 36 | */ 37 | export type RequireOneOrNone<ObjectType, KeysType extends keyof ObjectType = keyof ObjectType> = 38 | IfNotAnyOrNever<ObjectType, 39 | If<IsNever<KeysType>, 40 | ObjectType, 41 | _RequireOneOrNone<ObjectType, If<IsAny<KeysType>, keyof ObjectType, KeysType>> 42 | >>; 43 | 44 | type _RequireOneOrNone<ObjectType, KeysType extends keyof ObjectType> = ( 45 | | RequireExactlyOne<ObjectType, KeysType> 46 | | RequireNone<KeysType> 47 | ) & Omit<ObjectType, KeysType>; // Ignore unspecified keys. 48 | -------------------------------------------------------------------------------- /source/required-keys-of.d.ts: -------------------------------------------------------------------------------- 1 | import type {OptionalKeysOf} from './optional-keys-of.d.ts'; 2 | 3 | /** 4 | Extract all required keys from the given type. 5 | 6 | This is useful when you want to create a new type that contains different type values for the required keys only or use the list of keys for validation purposes, etc... 7 | 8 | @example 9 | ``` 10 | import type {RequiredKeysOf} from 'type-fest'; 11 | 12 | declare function createValidation<Entity extends object, Key extends RequiredKeysOf<Entity> = RequiredKeysOf<Entity>>(field: Key, validator: (value: Entity[Key]) => boolean): ValidatorFn; 13 | 14 | interface User { 15 | name: string; 16 | surname: string; 17 | 18 | luckyNumber?: number; 19 | } 20 | 21 | const validator1 = createValidation<User>('name', value => value.length < 25); 22 | const validator2 = createValidation<User>('surname', value => value.length < 25); 23 | ``` 24 | 25 | @category Utilities 26 | */ 27 | export type RequiredKeysOf<BaseType extends object> = 28 | BaseType extends unknown // For distributing `BaseType` 29 | ? Exclude<keyof BaseType, OptionalKeysOf<BaseType>> 30 | : never; // Should never happen 31 | -------------------------------------------------------------------------------- /source/screaming-snake-case.d.ts: -------------------------------------------------------------------------------- 1 | import type {DefaultDelimiterCaseOptions} from './delimiter-case.d.ts'; 2 | import type {ApplyDefaultOptions} from './internal/index.d.ts'; 3 | import type {SnakeCase} from './snake-case.d.ts'; 4 | import type {WordsOptions} from './words.d.ts'; 5 | 6 | /** 7 | Convert a string literal to screaming-snake-case. 8 | 9 | This can be useful when, for example, converting a camel-cased object property to a screaming-snake-cased SQL column name. 10 | 11 | @example 12 | ``` 13 | import type {ScreamingSnakeCase} from 'type-fest'; 14 | 15 | const someVariable: ScreamingSnakeCase<'fooBar'> = 'FOO_BAR'; 16 | const someVariableNoSplitOnNumbers: ScreamingSnakeCase<'p2pNetwork', {splitOnNumbers: false}> = 'P2P_NETWORK'; 17 | 18 | ``` 19 | 20 | @category Change case 21 | @category Template literal 22 | */ 23 | export type ScreamingSnakeCase< 24 | Value, 25 | Options extends WordsOptions = {}, 26 | > = Value extends string 27 | ? Uppercase<SnakeCase<Value, ApplyDefaultOptions<WordsOptions, DefaultDelimiterCaseOptions, Options>>> 28 | : Value; 29 | -------------------------------------------------------------------------------- /source/set-non-nullable.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | Create a type that makes the given keys non-nullable, where the remaining keys are kept as is. 3 | 4 | If no keys are given, all keys will be made non-nullable. 5 | 6 | Use-case: You want to define a single model where the only thing that changes is whether or not some or all of the keys are non-nullable. 7 | 8 | @example 9 | ``` 10 | import type {SetNonNullable} from 'type-fest'; 11 | 12 | type Foo = { 13 | a: number | null; 14 | b: string | undefined; 15 | c?: boolean | null; 16 | } 17 | 18 | type SomeNonNullable = SetNonNullable<Foo, 'b' | 'c'>; 19 | // type SomeNonNullable = { 20 | // a: number | null; 21 | // b: string; // Can no longer be undefined. 22 | // c?: boolean; // Can no longer be null, but is still optional. 23 | // } 24 | 25 | type AllNonNullable = SetNonNullable<Foo>; 26 | // type AllNonNullable = { 27 | // a: number; // Can no longer be null. 28 | // b: string; // Can no longer be undefined. 29 | // c?: boolean; // Can no longer be null, but is still optional. 30 | // } 31 | ``` 32 | 33 | @category Object 34 | */ 35 | export type SetNonNullable<BaseType, Keys extends keyof BaseType = keyof BaseType> = { 36 | [Key in keyof BaseType]: Key extends Keys 37 | ? NonNullable<BaseType[Key]> 38 | : BaseType[Key]; 39 | }; 40 | -------------------------------------------------------------------------------- /source/set-optional.d.ts: -------------------------------------------------------------------------------- 1 | import type {Except} from './except.d.ts'; 2 | import type {HomomorphicPick} from './internal/index.d.ts'; 3 | import type {Simplify} from './simplify.d.ts'; 4 | 5 | /** 6 | Create a type that makes the given keys optional. The remaining keys are kept as is. The sister of the `SetRequired` type. 7 | 8 | Use-case: You want to define a single model where the only thing that changes is whether or not some of the keys are optional. 9 | 10 | @example 11 | ``` 12 | import type {SetOptional} from 'type-fest'; 13 | 14 | type Foo = { 15 | a: number; 16 | b?: string; 17 | c: boolean; 18 | } 19 | 20 | type SomeOptional = SetOptional<Foo, 'b' | 'c'>; 21 | // type SomeOptional = { 22 | // a: number; 23 | // b?: string; // Was already optional and still is. 24 | // c?: boolean; // Is now optional. 25 | // } 26 | ``` 27 | 28 | @category Object 29 | */ 30 | export type SetOptional<BaseType, Keys extends keyof BaseType> = 31 | (BaseType extends (...arguments_: never) => any 32 | ? (...arguments_: Parameters<BaseType>) => ReturnType<BaseType> 33 | : unknown) 34 | & _SetOptional<BaseType, Keys>; 35 | 36 | type _SetOptional<BaseType, Keys extends keyof BaseType> = 37 | BaseType extends unknown // To distribute `BaseType` when it's a union type. 38 | ? Simplify< 39 | // Pick just the keys that are readonly from the base type. 40 | Except<BaseType, Keys> & 41 | // Pick the keys that should be mutable from the base type and make them mutable. 42 | Partial<HomomorphicPick<BaseType, Keys>> 43 | > 44 | : never; 45 | -------------------------------------------------------------------------------- /source/set-readonly.d.ts: -------------------------------------------------------------------------------- 1 | import type {Except} from './except.d.ts'; 2 | import type {HomomorphicPick} from './internal/index.d.ts'; 3 | import type {Simplify} from './simplify.d.ts'; 4 | 5 | /** 6 | Create a type that makes the given keys readonly. The remaining keys are kept as is. 7 | 8 | Use-case: You want to define a single model where the only thing that changes is whether or not some of the keys are readonly. 9 | 10 | @example 11 | ``` 12 | import type {SetReadonly} from 'type-fest'; 13 | 14 | type Foo = { 15 | a: number; 16 | readonly b: string; 17 | c: boolean; 18 | } 19 | 20 | type SomeReadonly = SetReadonly<Foo, 'b' | 'c'>; 21 | // type SomeReadonly = { 22 | // a: number; 23 | // readonly b: string; // Was already readonly and still is. 24 | // readonly c: boolean; // Is now readonly. 25 | // } 26 | ``` 27 | 28 | @category Object 29 | */ 30 | export type SetReadonly<BaseType, Keys extends keyof BaseType> = 31 | (BaseType extends (...arguments_: never) => any 32 | ? (...arguments_: Parameters<BaseType>) => ReturnType<BaseType> 33 | : unknown) 34 | & _SetReadonly<BaseType, Keys>; 35 | 36 | export type _SetReadonly<BaseType, Keys extends keyof BaseType> = 37 | BaseType extends unknown // To distribute `BaseType` when it's a union type. 38 | ? Simplify< 39 | Except<BaseType, Keys> & 40 | Readonly<HomomorphicPick<BaseType, Keys>> 41 | > 42 | : never; 43 | -------------------------------------------------------------------------------- /source/set-return-type.d.ts: -------------------------------------------------------------------------------- 1 | import type {IsUnknown} from './is-unknown.d.ts'; 2 | 3 | /** 4 | Create a function type with a return type of your choice and the same parameters as the given function type. 5 | 6 | Use-case: You want to define a wrapped function that returns something different while receiving the same parameters. For example, you might want to wrap a function that can throw an error into one that will return `undefined` instead. 7 | 8 | @example 9 | ``` 10 | import type {SetReturnType} from 'type-fest'; 11 | 12 | type MyFunctionThatCanThrow = (foo: SomeType, bar: unknown) => SomeOtherType; 13 | 14 | type MyWrappedFunction = SetReturnType<MyFunctionThatCanThrow, SomeOtherType | undefined>; 15 | //=> type MyWrappedFunction = (foo: SomeType, bar: unknown) => SomeOtherType | undefined; 16 | ``` 17 | 18 | @category Function 19 | */ 20 | export type SetReturnType<Function_ extends (...arguments_: any[]) => any, TypeToReturn> = 21 | // Just using `Parameters<Fn>` isn't ideal because it doesn't handle the `this` fake parameter. 22 | Function_ extends (this: infer ThisArgument, ...arguments_: infer Arguments) => any ? ( 23 | // If a function did not specify the `this` fake parameter, it will be inferred to `unknown`. 24 | // We want to detect this situation just to display a friendlier type upon hovering on an IntelliSense-powered IDE. 25 | IsUnknown<ThisArgument> extends true ? (...arguments_: Arguments) => TypeToReturn : (this: ThisArgument, ...arguments_: Arguments) => TypeToReturn 26 | ) : ( 27 | // This part should be unreachable, but we make it meaningful just in case… 28 | (...arguments_: Parameters<Function_>) => TypeToReturn 29 | ); 30 | -------------------------------------------------------------------------------- /source/single-key-object.d.ts: -------------------------------------------------------------------------------- 1 | import type {IsEmptyObject} from './empty-object.d.ts'; 2 | import type {If} from './if.d.ts'; 3 | import type {IsUnion} from './is-union.d.ts'; 4 | 5 | /** 6 | Create a type that only accepts an object with a single key. 7 | 8 | @example 9 | ``` 10 | import type {SingleKeyObject} from 'type-fest'; 11 | 12 | const someFunction = <T>(parameter: SingleKeyObject<T>) => {}; 13 | 14 | someFunction({ 15 | value: true 16 | }); 17 | 18 | someFunction({ 19 | value: true, 20 | otherKey: true 21 | }); 22 | // Error: Argument of type '{value: boolean; otherKey: boolean}' is not assignable to parameter of type 'never'.ts(2345) 23 | ``` 24 | 25 | @category Object 26 | */ 27 | export type SingleKeyObject<ObjectType> = 28 | IsUnion<keyof ObjectType> extends true 29 | ? never 30 | : If<IsEmptyObject<ObjectType>, never, ObjectType>; 31 | -------------------------------------------------------------------------------- /source/snake-case.d.ts: -------------------------------------------------------------------------------- 1 | import type {DefaultDelimiterCaseOptions, DelimiterCase} from './delimiter-case.d.ts'; 2 | import type {ApplyDefaultOptions} from './internal/index.d.ts'; 3 | import type {WordsOptions} from './words.d.ts'; 4 | 5 | /** 6 | Convert a string literal to snake-case. 7 | 8 | This can be useful when, for example, converting a camel-cased object property to a snake-cased SQL column name. 9 | 10 | @example 11 | ``` 12 | import type {SnakeCase} from 'type-fest'; 13 | 14 | // Simple 15 | 16 | const someVariable: SnakeCase<'fooBar'> = 'foo_bar'; 17 | const noSplitOnNumbers: SnakeCase<'p2pNetwork'> = 'p2p_network'; 18 | const splitOnNumbers: SnakeCase<'p2pNetwork', {splitOnNumbers: true}> = 'p_2_p_network'; 19 | 20 | // Advanced 21 | 22 | type SnakeCasedProperties<T> = { 23 | [K in keyof T as SnakeCase<K>]: T[K] 24 | }; 25 | 26 | interface ModelProps { 27 | isHappy: boolean; 28 | fullFamilyName: string; 29 | foo: number; 30 | } 31 | 32 | const dbResult: SnakeCasedProperties<ModelProps> = { 33 | 'is_happy': true, 34 | 'full_family_name': 'Carla Smith', 35 | foo: 123 36 | }; 37 | ``` 38 | 39 | @category Change case 40 | @category Template literal 41 | */ 42 | export type SnakeCase< 43 | Value, 44 | Options extends WordsOptions = {}, 45 | > = DelimiterCase<Value, '_', ApplyDefaultOptions<WordsOptions, DefaultDelimiterCaseOptions, Options>>; 46 | -------------------------------------------------------------------------------- /source/snake-cased-properties-deep.d.ts: -------------------------------------------------------------------------------- 1 | import type {DefaultDelimiterCaseOptions} from './delimiter-case.d.ts'; 2 | import type {DelimiterCasedPropertiesDeep} from './delimiter-cased-properties-deep.d.ts'; 3 | import type {ApplyDefaultOptions} from './internal/index.d.ts'; 4 | import type {WordsOptions} from './words.d.ts'; 5 | 6 | /** 7 | Convert object properties to snake case recursively. 8 | 9 | This can be useful when, for example, converting some API types from a different style. 10 | 11 | @see SnakeCase 12 | @see SnakeCasedProperties 13 | 14 | @example 15 | ``` 16 | import type {SnakeCasedPropertiesDeep} from 'type-fest'; 17 | 18 | interface User { 19 | userId: number; 20 | userName: string; 21 | } 22 | 23 | interface UserWithFriends { 24 | userInfo: User; 25 | userFriends: User[]; 26 | } 27 | 28 | const result: SnakeCasedPropertiesDeep<UserWithFriends> = { 29 | user_info: { 30 | user_id: 1, 31 | user_name: 'Tom', 32 | }, 33 | user_friends: [ 34 | { 35 | user_id: 2, 36 | user_name: 'Jerry', 37 | }, 38 | { 39 | user_id: 3, 40 | user_name: 'Spike', 41 | }, 42 | ], 43 | }; 44 | 45 | const splitOnNumbers: SnakeCasedPropertiesDeep<{line1: { line2: [{ line3: string }] }}, {splitOnNumbers: true}> = { 46 | line_1: { 47 | line_2: [ 48 | { 49 | line_3: 'string', 50 | }, 51 | ], 52 | }, 53 | }; 54 | ``` 55 | 56 | @category Change case 57 | @category Template literal 58 | @category Object 59 | */ 60 | export type SnakeCasedPropertiesDeep< 61 | Value, 62 | Options extends WordsOptions = {}, 63 | > = DelimiterCasedPropertiesDeep<Value, '_', ApplyDefaultOptions<WordsOptions, DefaultDelimiterCaseOptions, Options>>; 64 | -------------------------------------------------------------------------------- /source/snake-cased-properties.d.ts: -------------------------------------------------------------------------------- 1 | import type {DefaultDelimiterCaseOptions} from './delimiter-case.d.ts'; 2 | import type {DelimiterCasedProperties} from './delimiter-cased-properties.d.ts'; 3 | import type {ApplyDefaultOptions} from './internal/index.d.ts'; 4 | import type {WordsOptions} from './words.d.ts'; 5 | 6 | /** 7 | Convert object properties to snake case but not recursively. 8 | 9 | This can be useful when, for example, converting some API types from a different style. 10 | 11 | @see SnakeCase 12 | @see SnakeCasedPropertiesDeep 13 | 14 | @example 15 | ``` 16 | import type {SnakeCasedProperties} from 'type-fest'; 17 | 18 | interface User { 19 | userId: number; 20 | userName: string; 21 | } 22 | 23 | const result: SnakeCasedProperties<User> = { 24 | user_id: 1, 25 | user_name: 'Tom', 26 | }; 27 | 28 | const splitOnNumbers: SnakeCasedProperties<{line1: string}, {splitOnNumbers: true}> = { 29 | 'line_1': 'string', 30 | }; 31 | ``` 32 | 33 | @category Change case 34 | @category Template literal 35 | @category Object 36 | */ 37 | export type SnakeCasedProperties< 38 | Value, 39 | Options extends WordsOptions = {}, 40 | > = DelimiterCasedProperties<Value, '_', ApplyDefaultOptions<WordsOptions, DefaultDelimiterCaseOptions, Options>>; 41 | -------------------------------------------------------------------------------- /source/string-repeat.d.ts: -------------------------------------------------------------------------------- 1 | import type {IsNumericLiteral} from './is-literal.d.ts'; 2 | import type {IsNegative} from './numeric.d.ts'; 3 | 4 | /** 5 | Returns a new string which contains the specified number of copies of a given string, just like `String#repeat()`. 6 | 7 | @example 8 | ``` 9 | import {StringRepeat} from 'type-fest'; 10 | 11 | declare function stringRepeat< 12 | Input extends string, 13 | Count extends number 14 | >(input: Input, count: Count): StringRepeat<Input, Count>; 15 | 16 | // The return type is the exact string literal, not just `string`. 17 | 18 | stringRepeat('foo', 2); 19 | //=> 'foofoo' 20 | 21 | stringRepeat('=', 3); 22 | //=> '===' 23 | ``` 24 | 25 | @category String 26 | @category Template literal 27 | */ 28 | export type StringRepeat< 29 | Input extends string, 30 | Count extends number, 31 | > = StringRepeatHelper<Input, Count>; 32 | 33 | type StringRepeatHelper< 34 | Input extends string, 35 | Count extends number, 36 | Counter extends never[] = [], 37 | Accumulator extends string = '', 38 | > = 39 | IsNegative<Count> extends true 40 | ? never 41 | : Input extends '' 42 | ? '' 43 | : Count extends Counter['length'] 44 | ? Accumulator 45 | : IsNumericLiteral<Count> extends false 46 | ? string 47 | : StringRepeatHelper<Input, Count, [...Counter, never], `${Accumulator}${Input}`>; 48 | -------------------------------------------------------------------------------- /source/string-slice.d.ts: -------------------------------------------------------------------------------- 1 | import type {Join} from './join.d.ts'; 2 | import type {ArraySlice} from './array-slice.d.ts'; 3 | import type {StringToArray} from './internal/index.d.ts'; 4 | 5 | /** 6 | Returns a string slice of a given range, just like `String#slice()`. 7 | 8 | @see {ArraySlice} 9 | 10 | @example 11 | ``` 12 | import type {StringSlice} from 'type-fest'; 13 | 14 | StringSlice<'abcde', 0, 2>; 15 | //=> 'ab' 16 | 17 | StringSlice<'abcde', 1>; 18 | //=> 'bcde' 19 | 20 | StringSlice<'abcde', 0, -1>; 21 | //=> 'abcd' 22 | 23 | StringSlice<'abcde', -2, -1>; 24 | //=> 'd' 25 | ``` 26 | 27 | @category String 28 | */ 29 | export type StringSlice< 30 | S extends string, 31 | Start extends number = 0, 32 | End extends number = StringToArray<S>['length'], 33 | > = string extends S 34 | ? string 35 | : ArraySlice<StringToArray<S>, Start, End> extends infer R extends readonly string[] 36 | ? Join<R, ''> 37 | : never; 38 | -------------------------------------------------------------------------------- /source/stringified.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | Create a type with the keys of the given type changed to `string` type. 3 | 4 | Use-case: Changing interface values to strings in order to use them in a form model. 5 | 6 | @example 7 | ``` 8 | import type {Stringified} from 'type-fest'; 9 | 10 | type Car = { 11 | model: string; 12 | speed: number; 13 | } 14 | 15 | const carForm: Stringified<Car> = { 16 | model: 'Foo', 17 | speed: '101' 18 | }; 19 | ``` 20 | 21 | @category Object 22 | */ 23 | export type Stringified<ObjectType> = {[KeyType in keyof ObjectType]: string}; 24 | -------------------------------------------------------------------------------- /source/tagged-union.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | Create a union of types that share a common discriminant property. 3 | 4 | Use-case: A shorter way to declare tagged unions with multiple members. 5 | 6 | @example 7 | ``` 8 | import type {TaggedUnion} from 'type-fest'; 9 | 10 | type Tagged<Fields extends Record<string, unknown> = TaggedUnion<'type', Fields> 11 | 12 | // The TaggedUnion utility reduces the amount of boilerplate needed to create a tagged union with multiple members, making the code more concise. 13 | type EventMessage = Tagged<{ 14 | OpenExternalUrl: { 15 | url: string; 16 | id: number; 17 | language: string; 18 | }; 19 | ToggleBackButtonVisibility: { 20 | visible: boolean; 21 | }; 22 | PurchaseButtonPressed: { 23 | price: number; 24 | time: Date; 25 | }; 26 | NavigationStateChanged: { 27 | navigation?: string; 28 | }; 29 | }>; 30 | 31 | // Here is the same type created without this utility. 32 | type EventMessage = 33 | | { 34 | type: 'OpenExternalUrl'; 35 | url: string; 36 | id: number; 37 | language: string; 38 | } 39 | | {type: 'ToggleBackButtonVisibility'; visible: boolean} 40 | | {type: 'PurchaseButtonPressed'; price: number; time: Date} 41 | | {type: 'NavigationStateChanged'; navigation?: string}; 42 | ``` 43 | 44 | @category Utilities 45 | */ 46 | export type TaggedUnion< 47 | TagKey extends string, 48 | UnionMembers extends Record<string, Record<string, unknown>>, 49 | > = { 50 | [Name in keyof UnionMembers]: {[Key in TagKey]: Name} & UnionMembers[Name]; 51 | }[keyof UnionMembers]; 52 | -------------------------------------------------------------------------------- /source/trim.d.ts: -------------------------------------------------------------------------------- 1 | import type {Whitespace} from './internal/index.d.ts'; 2 | 3 | /** 4 | Remove spaces from the left side. 5 | */ 6 | type TrimLeft<V extends string> = V extends `${Whitespace}${infer R}` ? TrimLeft<R> : V; 7 | 8 | /** 9 | Remove spaces from the right side. 10 | */ 11 | type TrimRight<V extends string> = V extends `${infer R}${Whitespace}` ? TrimRight<R> : V; 12 | 13 | /** 14 | Remove leading and trailing spaces from a string. 15 | 16 | @example 17 | ``` 18 | import type {Trim} from 'type-fest'; 19 | 20 | Trim<' foo '> 21 | //=> 'foo' 22 | ``` 23 | 24 | @category String 25 | @category Template literal 26 | */ 27 | export type Trim<V extends string> = TrimLeft<TrimRight<V>>; 28 | -------------------------------------------------------------------------------- /source/tuple-to-object.d.ts: -------------------------------------------------------------------------------- 1 | import type {IsTuple} from './is-tuple.d.ts'; 2 | import type {UnknownArray} from './unknown-array.d.ts'; 3 | import type {IsAny} from './is-any.d.ts'; 4 | import type {If} from './if.d.ts'; 5 | 6 | /** 7 | Transforms a tuple into an object, mapping each tuple index to its corresponding type as a key-value pair. 8 | 9 | Note: Tuple labels are [lost in the transformation process](https://stackoverflow.com/a/70398429/11719314). For example, `TupleToObject<[x: number, y: number]>` produces `{0: number; 1: number}`, and not `{x: number; y: number}`. 10 | 11 | @example 12 | ``` 13 | type Example1 = TupleToObject<[number, string, boolean]>; 14 | //=> { 0: number; 1: string; 2: boolean } 15 | 16 | // Tuples with optional indices 17 | type Example2 = TupleToObject<[number, string?, boolean?]>; 18 | //=> { 0: number; 1?: string; 2?: boolean } 19 | 20 | // Readonly tuples 21 | type Example3 = TupleToObject<readonly [number, string?]>; 22 | //=> { readonly 0: number; readonly 1?: string } 23 | 24 | // Non-tuple arrays get transformed into index signatures 25 | type Example4 = TupleToObject<string[]>; 26 | //=> { [x: number]: string } 27 | 28 | // Tuples with rest elements 29 | type Example5 = TupleToObject<[number, string, ...boolean[]]>; 30 | //=> { [x: number]: number | string | boolean; 0: number; 1: string } 31 | 32 | // Tuple labels are not preserved 33 | type Example6 = TupleToObject<[x: number, y: number]>; 34 | //=> { 0: number; 1: number } 35 | ``` 36 | 37 | @category Array 38 | */ 39 | export type TupleToObject<TArray extends UnknownArray> = If<IsAny<TArray>, any, { 40 | [ 41 | Key in keyof TArray as Key & (`${number}` | (IsTuple<TArray> extends true ? never : number)) 42 | ]: TArray[Key]; 43 | }>; 44 | -------------------------------------------------------------------------------- /source/typed-array.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | Matches any [typed array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray), like `Uint8Array` or `Float64Array`. 3 | 4 | @category Array 5 | */ 6 | export type TypedArray = 7 | | Int8Array 8 | | Uint8Array 9 | | Uint8ClampedArray 10 | | Int16Array 11 | | Uint16Array 12 | | Int32Array 13 | | Uint32Array 14 | | Float32Array 15 | | Float64Array 16 | | BigInt64Array 17 | | BigUint64Array; 18 | -------------------------------------------------------------------------------- /source/union-to-tuple.d.ts: -------------------------------------------------------------------------------- 1 | import type {IsNever} from './is-never.d.ts'; 2 | import type {UnionToIntersection} from './union-to-intersection.d.ts'; 3 | 4 | /** 5 | Returns the last element of a union type. 6 | 7 | @example 8 | ``` 9 | type Last = LastOfUnion<1 | 2 | 3>; 10 | //=> 3 11 | ``` 12 | */ 13 | type LastOfUnion<T> = 14 | UnionToIntersection<T extends any ? () => T : never> extends () => (infer R) 15 | ? R 16 | : never; 17 | 18 | /** 19 | Convert a union type into an unordered tuple type of its elements. 20 | 21 | "Unordered" means the elements of the tuple are not guaranteed to be in the same order as in the union type. The arrangement can appear random and may change at any time. 22 | 23 | This can be useful when you have objects with a finite set of keys and want a type defining only the allowed keys, but do not want to repeat yourself. 24 | 25 | @example 26 | ``` 27 | import type {UnionToTuple} from 'type-fest'; 28 | 29 | type Numbers = 1 | 2 | 3; 30 | type NumbersTuple = UnionToTuple<Numbers>; 31 | //=> [1, 2, 3] 32 | ``` 33 | 34 | @example 35 | ``` 36 | import type {UnionToTuple} from 'type-fest'; 37 | 38 | const pets = { 39 | dog: '🐶', 40 | cat: '🐱', 41 | snake: '🐍', 42 | }; 43 | 44 | type Pet = keyof typeof pets; 45 | //=> 'dog' | 'cat' | 'snake' 46 | 47 | const petList = Object.keys(pets) as UnionToTuple<Pet>; 48 | //=> ['dog', 'cat', 'snake'] 49 | ``` 50 | 51 | @category Array 52 | */ 53 | export type UnionToTuple<T, L = LastOfUnion<T>> = 54 | IsNever<T> extends false 55 | ? [...UnionToTuple<Exclude<T, L>>, L] 56 | : []; 57 | -------------------------------------------------------------------------------- /source/unknown-array.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | Represents an array with `unknown` value. 3 | 4 | Use case: You want a type that all arrays can be assigned to, but you don't care about the value. 5 | 6 | @example 7 | ``` 8 | import type {UnknownArray} from 'type-fest'; 9 | 10 | type IsArray<T> = T extends UnknownArray ? true : false; 11 | 12 | type A = IsArray<['foo']>; 13 | //=> true 14 | 15 | type B = IsArray<readonly number[]>; 16 | //=> true 17 | 18 | type C = IsArray<string>; 19 | //=> false 20 | ``` 21 | 22 | @category Type 23 | @category Array 24 | */ 25 | export type UnknownArray = readonly unknown[]; 26 | -------------------------------------------------------------------------------- /source/unknown-map.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | Represents a map with `unknown` key and value. 3 | 4 | Use case: You want a type that all maps can be assigned to, but you don't care about the value. 5 | 6 | @example 7 | ``` 8 | import type {UnknownMap} from 'type-fest'; 9 | 10 | type IsMap<T> = T extends UnknownMap ? true : false; 11 | 12 | type A = IsMap<Map<string, number>>; 13 | //=> true 14 | 15 | type B = IsMap<ReadonlyMap<number, string>>; 16 | //=> true 17 | 18 | type C = IsMap<string>; 19 | //=> false 20 | ``` 21 | 22 | @category Type 23 | */ 24 | export type UnknownMap = ReadonlyMap<unknown, unknown>; 25 | -------------------------------------------------------------------------------- /source/unknown-record.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | Represents an object with `unknown` value. You probably want this instead of `{}`. 3 | 4 | Use case: You have an object whose keys and values are unknown to you. 5 | 6 | @example 7 | ``` 8 | import type {UnknownRecord} from 'type-fest'; 9 | 10 | function toJson(object: UnknownRecord) { 11 | return JSON.stringify(object); 12 | } 13 | 14 | toJson({hello: 'world'}); 15 | //=> '{"hello":"world"}' 16 | 17 | function isObject(value: unknown): value is UnknownRecord { 18 | return typeof value === 'object' && value !== null; 19 | } 20 | 21 | isObject({hello: 'world'}); 22 | //=> true 23 | 24 | isObject('hello'); 25 | //=> false 26 | ``` 27 | 28 | @category Type 29 | @category Object 30 | */ 31 | export type UnknownRecord = Record<PropertyKey, unknown>; 32 | -------------------------------------------------------------------------------- /source/unknown-set.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | Represents a set with `unknown` value. 3 | 4 | Use case: You want a type that all sets can be assigned to, but you don't care about the value. 5 | 6 | @example 7 | ``` 8 | import type {UnknownSet} from 'type-fest'; 9 | 10 | type IsSet<T> = T extends UnknownSet ? true : false; 11 | 12 | type A = IsSet<Set<string>>; 13 | //=> true 14 | 15 | type B = IsSet<ReadonlySet<number>>; 16 | //=> true 17 | 18 | type C = IsSet<string>; 19 | //=> false 20 | ``` 21 | 22 | @category Type 23 | */ 24 | export type UnknownSet = ReadonlySet<unknown>; 25 | -------------------------------------------------------------------------------- /source/value-of.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | Create a union of the given object's values, and optionally specify which keys to get the values from. 3 | 4 | Please upvote [this issue](https://github.com/microsoft/TypeScript/issues/31438) if you want to have this type as a built-in in TypeScript. 5 | 6 | @example 7 | ``` 8 | // data.json 9 | { 10 | 'foo': 1, 11 | 'bar': 2, 12 | 'biz': 3 13 | } 14 | 15 | // main.ts 16 | import type {ValueOf} from 'type-fest'; 17 | import data = require('./data.json'); 18 | 19 | export function getData(name: string): ValueOf<typeof data> { 20 | return data[name]; 21 | } 22 | 23 | export function onlyBar(name: string): ValueOf<typeof data, 'bar'> { 24 | return data[name]; 25 | } 26 | 27 | // file.ts 28 | import {getData, onlyBar} from './main.d.ts'; 29 | 30 | getData('foo'); 31 | //=> 1 32 | 33 | onlyBar('foo'); 34 | //=> TypeError ... 35 | 36 | onlyBar('bar'); 37 | //=> 2 38 | ``` 39 | 40 | @category Object 41 | */ 42 | export type ValueOf<ObjectType, ValueType extends keyof ObjectType = keyof ObjectType> = ObjectType[ValueType]; 43 | -------------------------------------------------------------------------------- /source/writable-keys-of.d.ts: -------------------------------------------------------------------------------- 1 | import type {IsEqual} from './is-equal.d.ts'; 2 | 3 | /** 4 | Extract all writable keys from the given type. 5 | 6 | This is useful when you want to create a new type that contains writable keys only. 7 | 8 | @example 9 | ``` 10 | import type {WritableKeysOf} from 'type-fest'; 11 | 12 | interface User { 13 | name: string; 14 | surname: string; 15 | readonly id: number; 16 | } 17 | 18 | type UpdateRequest<Entity extends object> = Pick<Entity, WritableKeysOf<Entity>>; 19 | 20 | const update1: UpdateRequest<User> = { 21 | name: 'Alice', 22 | surname: 'Acme', 23 | }; 24 | ``` 25 | 26 | @category Utilities 27 | */ 28 | export type WritableKeysOf<T extends object> = 29 | T extends unknown // For distributing `T` 30 | ? (keyof { 31 | [P in keyof T as IsEqual<{[Q in P]: T[P]}, {readonly [Q in P]: T[P]}> extends false ? P : never]: never 32 | }) & keyof T // Intersect with `keyof T` to ensure result of `WritableKeysOf<T>` is always assignable to `keyof T` 33 | : never; // Should never happen 34 | -------------------------------------------------------------------------------- /test-d/and.ts: -------------------------------------------------------------------------------- 1 | import {expectType} from 'tsd'; 2 | import type {And} from '../source/and.d.ts'; 3 | 4 | expectType<And<true, true>>(true); 5 | expectType<And<true, false>>(false); 6 | expectType<And<false, true>>(false); 7 | expectType<And<false, false>>(false); 8 | 9 | expectType<And<true, boolean>>({} as boolean); 10 | expectType<And<boolean, true>>({} as boolean); 11 | expectType<And<false, boolean>>(false); 12 | expectType<And<boolean, false>>(false); 13 | expectType<And<boolean, boolean>>({} as boolean); 14 | 15 | // Boundary cases 16 | expectType<And<any, true>>({} as boolean); 17 | expectType<And<true, any>>({} as boolean); 18 | expectType<And<any, false>>(false); 19 | expectType<And<false, any>>(false); 20 | expectType<And<any, boolean>>({} as boolean); 21 | expectType<And<boolean, any>>({} as boolean); 22 | expectType<And<any, any>>({} as boolean); 23 | 24 | expectType<And<never, true>>(false); 25 | expectType<And<true, never>>(false); 26 | expectType<And<never, false>>(false); 27 | expectType<And<false, never>>(false); 28 | expectType<And<never, boolean>>(false); 29 | expectType<And<boolean, never>>(false); 30 | expectType<And<never, never>>(false); 31 | -------------------------------------------------------------------------------- /test-d/array-indices.ts: -------------------------------------------------------------------------------- 1 | import {expectNotAssignable, expectType, expectAssignable} from 'tsd'; 2 | import type {ArrayIndices} from '../index.d.ts'; 3 | 4 | const values = ['a', 'b', 'c'] as const; 5 | type ValueKeys = ArrayIndices<typeof values>; 6 | 7 | declare const test: 0 | 1 | 2; 8 | expectType<ValueKeys>(test); 9 | 10 | expectAssignable<ValueKeys>(0); 11 | expectAssignable<ValueKeys>(1); 12 | expectAssignable<ValueKeys>(2); 13 | 14 | expectNotAssignable<ValueKeys>(-1); 15 | expectNotAssignable<ValueKeys>(3); 16 | 17 | type TupleKeys = ArrayIndices<['a', 2]>; 18 | 19 | declare const testTuple: 0 | 1; 20 | expectType<TupleKeys>(testTuple); 21 | 22 | expectAssignable<TupleKeys>(0); 23 | expectAssignable<TupleKeys>(1); 24 | 25 | expectNotAssignable<TupleKeys>(-1); 26 | expectNotAssignable<TupleKeys>(2); 27 | -------------------------------------------------------------------------------- /test-d/array-splice.ts: -------------------------------------------------------------------------------- 1 | import {expectType} from 'tsd'; 2 | import type {ArraySplice} from '../index.d.ts'; 3 | 4 | // Test fixed array 5 | type TestTuple = ['a', 'b', 'c', 'd']; 6 | 7 | declare const tuple0: ArraySplice<TestTuple, 2, 1>; 8 | expectType<['a', 'b', 'd']>(tuple0); 9 | 10 | declare const tuple1: ArraySplice<TestTuple, 2, 1, ['e', 'f']>; 11 | expectType<['a', 'b', 'e', 'f', 'd']>(tuple1); 12 | 13 | declare const tuple2: ArraySplice<TestTuple, 2, 0, ['e', 'f']>; 14 | expectType<['a', 'b', 'e', 'f', 'c', 'd']>(tuple2); 15 | 16 | declare const tuple3: ArraySplice<TestTuple, 0, 1>; 17 | expectType<['b', 'c', 'd']>(tuple3); 18 | 19 | declare const tuple4: ArraySplice<TestTuple, 0, 1, ['e', 'f']>; 20 | expectType<['e', 'f', 'b', 'c', 'd']>(tuple4); 21 | 22 | // Test variable array 23 | type TestArray = ['a', 'b', 'c', 'd', ...number[]]; 24 | 25 | declare const array0: ArraySplice<TestArray, 2, 1>; 26 | expectType<['a', 'b', 'd', ...number[]]>(array0); 27 | 28 | declare const array1: ArraySplice<TestArray, 2, 1, ['e', 'f']>; 29 | expectType<['a', 'b', 'e', 'f', 'd', ...number[]]>(array1); 30 | 31 | declare const array2: ArraySplice<TestArray, 6, 0, ['e', 'f']>; 32 | expectType<['a', 'b', 'c', 'd', number, number, 'e', 'f', ...number[]]>(array2); 33 | -------------------------------------------------------------------------------- /test-d/array-values.ts: -------------------------------------------------------------------------------- 1 | import {expectNotAssignable, expectType, expectAssignable} from 'tsd'; 2 | import type {ArrayValues} from '../index.d.ts'; 3 | 4 | const values = ['a', 'b', 'c'] as const; 5 | type Values = ArrayValues<typeof values>; 6 | 7 | declare const test: 'a' | 'b' | 'c'; 8 | expectType<Values>(test); 9 | 10 | expectAssignable<Values>('a'); 11 | expectAssignable<Values>('b'); 12 | expectAssignable<Values>('c'); 13 | 14 | expectNotAssignable<Values>(''); 15 | expectNotAssignable<Values>(0); 16 | 17 | type TupleValues = ArrayValues<['1', 2, {c: true}]>; 18 | 19 | declare const testTuple: '1' | 2 | {c: true}; 20 | expectType<TupleValues>(testTuple); 21 | 22 | expectAssignable<TupleValues>('1'); 23 | expectAssignable<TupleValues>(2); 24 | expectAssignable<TupleValues>({c: true}); 25 | 26 | expectNotAssignable<TupleValues>({}); 27 | expectNotAssignable<TupleValues>(1); 28 | expectNotAssignable<TupleValues>('2'); 29 | 30 | type AnyStringValues = ArrayValues<string[]>; 31 | expectAssignable<AnyStringValues>(''); 32 | expectAssignable<AnyStringValues>('123'); 33 | expectNotAssignable<AnyStringValues>(123); 34 | expectNotAssignable<AnyStringValues>(undefined); 35 | expectNotAssignable<AnyStringValues>(null); 36 | -------------------------------------------------------------------------------- /test-d/arrayable.ts: -------------------------------------------------------------------------------- 1 | import {expectType} from 'tsd'; 2 | import type {Arrayable} from '../index.d.ts'; 3 | 4 | declare const unknown: unknown; 5 | 6 | expectType<Arrayable<string>>(unknown as string | string[]); 7 | expectType<Arrayable<string | {foo: number}>>(unknown as (string | {foo: number}) | Array<string | {foo: number}>); 8 | expectType<Arrayable<never>>(unknown as /* never | */ never[]); 9 | expectType<Arrayable<string[]>>(unknown as string[] | string[][]); 10 | 11 | // Test for issue https://github.com/sindresorhus/type-fest/issues/952 12 | type Item = number; 13 | function castArray1(value: Arrayable<Item>): Item[] { 14 | return Array.isArray(value) ? value : [value]; 15 | } 16 | 17 | expectType<Item[]>(unknown as ReturnType<typeof castArray1>); 18 | 19 | function castArray2<T>(value: Arrayable<T>): T[] { 20 | return Array.isArray(value) ? value : [value]; 21 | } 22 | 23 | expectType<number[]>(castArray2(1)); 24 | expectType<number[]>(castArray2([1, 2, 3])); 25 | -------------------------------------------------------------------------------- /test-d/async-return-type.ts: -------------------------------------------------------------------------------- 1 | import {expectNotAssignable, expectType} from 'tsd'; 2 | import type {AsyncReturnType} from '../index.d.ts'; 3 | 4 | async function asyncFunction(): Promise<number> { 5 | return 2; 6 | } 7 | 8 | type Value = AsyncReturnType<typeof asyncFunction>; 9 | 10 | asyncFunction().then(value => { // eslint-disable-line unicorn/prefer-top-level-await, @typescript-eslint/no-floating-promises 11 | expectType<Value>(value); 12 | expectNotAssignable<string>(value); 13 | }); 14 | 15 | function asyncPromiseLike(): PromiseLike<number> { 16 | return Promise.resolve(2); 17 | } 18 | 19 | type ValuePromiseLike = AsyncReturnType<typeof asyncPromiseLike>; 20 | 21 | asyncPromiseLike().then(value => { // eslint-disable-line unicorn/prefer-top-level-await, @typescript-eslint/no-floating-promises 22 | expectType<ValuePromiseLike>(value); 23 | expectNotAssignable<string>(value); 24 | }); 25 | -------------------------------------------------------------------------------- /test-d/asyncify.ts: -------------------------------------------------------------------------------- 1 | import {expectType} from 'tsd'; 2 | import type {Asyncify} from '../index.d.ts'; 3 | 4 | declare function getFooSync(name: string): RegExp; 5 | declare function getFooWithThisArgumentSync(this: Date, name: string): RegExp; 6 | 7 | // Basic usage. 8 | declare const getFooAsync1: Asyncify<typeof getFooSync>; 9 | expectType<(name: string) => Promise<RegExp>>(getFooAsync1); 10 | 11 | // Noops with async functions. 12 | declare const getFooAsync2: Asyncify<typeof getFooAsync1>; 13 | expectType<typeof getFooAsync1>(getFooAsync2); 14 | 15 | // Respects `thisArg`. 16 | declare const getFooWithThisArgumentAsync1: Asyncify<typeof getFooWithThisArgumentSync>; 17 | const callResult = getFooWithThisArgumentAsync1.call(new Date(), 'foo'); 18 | expectType<Promise<RegExp>>(callResult); 19 | 20 | // @ts-expect-error 21 | // eslint-disable-next-line @typescript-eslint/no-floating-promises 22 | getFooWithThisArgumentAsync1.call('not-date', 'foo'); 23 | -------------------------------------------------------------------------------- /test-d/camel-cased-properties.ts: -------------------------------------------------------------------------------- 1 | import {expectType} from 'tsd'; 2 | import type {CamelCasedProperties} from '../index.d.ts'; 3 | 4 | declare const foo: CamelCasedProperties<{A: number; B: {C: string}}>; 5 | expectType<{a: number; b: {C: string}}>(foo); 6 | 7 | declare const bar: CamelCasedProperties<Array<{helloWorld: string}>>; 8 | expectType<Array<{helloWorld: string}>>(bar); 9 | 10 | declare const fooBar: CamelCasedProperties<() => {a: string}>; 11 | expectType<() => {a: string}>(fooBar); 12 | 13 | declare const baz: CamelCasedProperties<{fooBAR: number; BARFoo: string}, {preserveConsecutiveUppercase: true}>; 14 | expectType<{fooBAR: number; bARFoo: string}>(baz); 15 | 16 | declare const biz: CamelCasedProperties<{fooBAR: number; BARFoo: string}>; 17 | expectType<{fooBar: number; barFoo: string}>(biz); 18 | 19 | // Verify example 20 | type User = { 21 | UserId: number; 22 | UserName: string; 23 | }; 24 | 25 | const result: CamelCasedProperties<User> = { 26 | userId: 1, 27 | userName: 'Tom', 28 | }; 29 | expectType<CamelCasedProperties<User>>(result); 30 | -------------------------------------------------------------------------------- /test-d/conditional-except.ts: -------------------------------------------------------------------------------- 1 | import {expectType} from 'tsd'; 2 | import type {ConditionalExcept, Primitive} from '../index.d.ts'; 3 | 4 | class Awesome { 5 | name!: string; 6 | successes!: number; 7 | failures!: bigint; 8 | 9 | run(): void { 10 | // Empty 11 | } 12 | } 13 | 14 | type Example = { 15 | a: string; 16 | b?: string | number; 17 | c?: string; 18 | d: Record<string, unknown>; 19 | }; 20 | 21 | declare const exampleConditionalExcept: ConditionalExcept<Example, string>; 22 | expectType<{b?: string | number; c?: string; d: Record<string, unknown>}>(exampleConditionalExcept); 23 | 24 | declare const awesomeConditionalExcept: ConditionalExcept<Awesome, Primitive>; 25 | expectType<{run: () => void}>(awesomeConditionalExcept); 26 | 27 | declare const exampleConditionalExceptWithUndefined: ConditionalExcept<Example, string | undefined>; 28 | expectType<{b?: string | number; d: Record<string, unknown>}>(exampleConditionalExceptWithUndefined); 29 | -------------------------------------------------------------------------------- /test-d/conditional-pick.ts: -------------------------------------------------------------------------------- 1 | import {expectType} from 'tsd'; 2 | import type {ConditionalPick, Primitive} from '../index.d.ts'; 3 | 4 | class Awesome { 5 | name!: string; 6 | successes!: number; 7 | failures!: bigint; 8 | 9 | run(): void { 10 | // Empty 11 | } 12 | } 13 | 14 | type Example = { 15 | a: string; 16 | b?: string | number; 17 | c?: string; 18 | d: Record<string, unknown>; 19 | }; 20 | 21 | declare const exampleConditionalPick: ConditionalPick<Example, string>; 22 | expectType<{a: string}>(exampleConditionalPick); 23 | 24 | declare const awesomeConditionalPick: ConditionalPick<Awesome, Primitive>; 25 | expectType<{name: string; successes: number; failures: bigint}>(awesomeConditionalPick); 26 | 27 | declare const exampleConditionalPickWithUndefined: ConditionalPick<Example, string | undefined>; 28 | expectType<{a: string; c?: string}>(exampleConditionalPickWithUndefined); 29 | -------------------------------------------------------------------------------- /test-d/conditional-simplify.ts: -------------------------------------------------------------------------------- 1 | import {expectNotAssignable, expectType} from 'tsd'; 2 | import type {ConditionalSimplify} from '../index.d.ts'; 3 | 4 | type Position = {top: number; left: number}; 5 | type Size = {width: number; height: number}; 6 | 7 | // In your editor, hovering over `PositionAndSizeSimplified` will show a simplified object with all the properties. 8 | type PositionAndSizeIntersection = Position & Size; 9 | type PositionAndSizeSimplified = ConditionalSimplify<PositionAndSizeIntersection>; 10 | 11 | const position = {top: 120, left: 240}; 12 | const size = {width: 480, height: 600}; 13 | const positionAndSize = {...position, ...size}; 14 | expectType<PositionAndSizeSimplified>(positionAndSize); 15 | 16 | // Exclude function type to be simplified. 17 | type SomeFunction = (type: string) => string; 18 | type SimplifiedFunctionFail = ConditionalSimplify<SomeFunction>; // Return '{}' 19 | type SimplifiedFunctionPass = ConditionalSimplify<SomeFunction, Function>; // Return '(type: string) => string' 20 | 21 | declare const simplifiedFunctionFail: SimplifiedFunctionFail; 22 | declare const simplifiedFunctionPass: SimplifiedFunctionPass; 23 | 24 | expectNotAssignable<SomeFunction>(simplifiedFunctionFail); 25 | expectType<SomeFunction>(simplifiedFunctionPass); 26 | -------------------------------------------------------------------------------- /test-d/delimiter-cased-properties.ts: -------------------------------------------------------------------------------- 1 | import {expectType} from 'tsd'; 2 | import type {DelimiterCasedProperties} from '../index.d.ts'; 3 | 4 | declare const foo: DelimiterCasedProperties<{helloWorld: {fooBar: string}}, '/'>; 5 | expectType<{'hello/world': {fooBar: string}}>(foo); 6 | 7 | declare const bar: DelimiterCasedProperties<Array<{helloWorld: string}>, '-'>; 8 | expectType<Array<{helloWorld: string}>>(bar); 9 | 10 | declare const fooBar: DelimiterCasedProperties<() => {a: string}, '-'>; 11 | expectType<() => {a: string}>(fooBar); 12 | 13 | declare const withOptions: DelimiterCasedProperties<{helloWorld1: {fooBar: string}}, '.', {splitOnNumbers: true}>; 14 | expectType<{'hello.world.1': {fooBar: string}}>(withOptions); 15 | 16 | // Verify example 17 | type User = { 18 | userId: number; 19 | userName: string; 20 | }; 21 | const result: DelimiterCasedProperties<User, '-'> = { 22 | 'user-id': 1, 23 | 'user-name': 'Tom', 24 | }; 25 | expectType<DelimiterCasedProperties<User, '-'>>(result); 26 | -------------------------------------------------------------------------------- /test-d/empty-object.ts: -------------------------------------------------------------------------------- 1 | import {expectAssignable, expectType} from 'tsd'; 2 | import type {EmptyObject, IsEmptyObject} from '../index.d.ts'; 3 | 4 | declare let foo: EmptyObject; 5 | 6 | expectAssignable<{}>(foo); 7 | expectAssignable<{}>(foo = {}); 8 | 9 | // @ts-expect-error 10 | foo = []; 11 | // @ts-expect-error 12 | foo = {x: 1}; 13 | // @ts-expect-error 14 | foo = 42; 15 | // @ts-expect-error 16 | foo = null; 17 | // @ts-expect-error 18 | foo.bar = 42; 19 | // @ts-expect-error 20 | foo.bar = {}; 21 | 22 | expectType<IsEmptyObject<{}>>(true); 23 | expectType<IsEmptyObject<typeof foo>>(true); 24 | 25 | expectType<IsEmptyObject<[]>>(false); 26 | expectType<IsEmptyObject<null>>(false); 27 | expectType<IsEmptyObject<() => void>>(false); 28 | 29 | type Union = EmptyObject | {id: number}; 30 | 31 | const bar: Union = {}; 32 | // @ts-expect-error 33 | const _a: unknown = bar.id; 34 | 35 | const baz: Union = {id: 42}; 36 | expectType<{id: number}>(baz); 37 | -------------------------------------------------------------------------------- /test-d/enforce-optional.ts: -------------------------------------------------------------------------------- 1 | import {expectType} from 'tsd'; 2 | import type {EnforceOptional} from '../source/enforce-optional.d.ts'; 3 | 4 | type Foo = { 5 | a: string; 6 | b?: string; 7 | c: undefined; 8 | d: number | undefined; 9 | }; 10 | 11 | type EnforcedOptionalFoo = EnforceOptional<Foo>; 12 | 13 | declare const enforcedOptionalFoo: EnforcedOptionalFoo; 14 | 15 | expectType<{ 16 | a: string; 17 | b?: string; 18 | c: undefined; 19 | d?: number; 20 | }>(enforcedOptionalFoo); 21 | -------------------------------------------------------------------------------- /test-d/except.ts: -------------------------------------------------------------------------------- 1 | import {expectType} from 'tsd'; 2 | import type {Except} from '../index.d.ts'; 3 | 4 | declare const except: Except<{a: number; b: string}, 'b'>; 5 | expectType<{a: number}>(except); 6 | // @ts-expect-error 7 | const _a: unknown = except.b; 8 | 9 | const nonStrict = { 10 | a: 1, 11 | b: '2', 12 | }; 13 | 14 | const nonStrictAssignment: typeof except = nonStrict; // No error 15 | 16 | declare const strictExcept: Except<{a: number; b: string}, 'b', {requireExactProps: true}>; 17 | 18 | // @ts-expect-error 19 | const strictAssignment: typeof strictExcept = nonStrict; 20 | 21 | // Generic properties 22 | type Example = { 23 | [key: string]: unknown; 24 | foo: number; 25 | bar: string; 26 | }; 27 | 28 | const test: Except<Example, 'bar', {requireExactProps: false}> = {foo: 123, bar: 'asdf'}; 29 | expectType<number>(test.foo); 30 | expectType<unknown>(test['bar']); 31 | -------------------------------------------------------------------------------- /test-d/fixed-length-array.ts: -------------------------------------------------------------------------------- 1 | import {expectAssignable, expectNotAssignable} from 'tsd'; 2 | import type {FixedLengthArray} from '../index.d.ts'; 3 | 4 | type FixedToThreeStrings = FixedLengthArray<string, 3>; 5 | 6 | expectAssignable<FixedToThreeStrings>(['a', 'b', 'c']); 7 | 8 | expectNotAssignable<FixedToThreeStrings>(['a', 'b', 123]); 9 | expectNotAssignable<FixedToThreeStrings>(['a']); 10 | expectNotAssignable<FixedToThreeStrings>(['a', 'b']); 11 | expectNotAssignable<FixedToThreeStrings>(['a', 'b', 'c', 'd']); 12 | -------------------------------------------------------------------------------- /test-d/global-this.ts: -------------------------------------------------------------------------------- 1 | import {expectType} from 'tsd'; 2 | import type {GlobalThis} from '../index.d.ts'; 3 | 4 | type ExtraProperties = GlobalThis & { 5 | readonly GLOBAL_TOKEN: string; 6 | }; 7 | 8 | // Verify `globalThis` can be cast to a type which extends `GlobalThis`. 9 | expectType<string>((globalThis as ExtraProperties).GLOBAL_TOKEN); 10 | 11 | // Verify that object literals cannot be cast to a type which extends `GlobalThis`. 12 | declare function consumeExtraProperties(extraProperties: ExtraProperties): void; 13 | // @ts-expect-error 14 | consumeExtraProperties(({something: 'value'}) as ExtraProperties); 15 | -------------------------------------------------------------------------------- /test-d/greater-than.ts: -------------------------------------------------------------------------------- 1 | import {expectType} from 'tsd'; 2 | import type {GreaterThan} from '../index.d.ts'; 3 | import type {NegativeInfinity, PositiveInfinity} from '../source/numeric.d.ts'; 4 | 5 | declare const never: never; 6 | 7 | expectType<GreaterThan<1, 2>>(false); 8 | expectType<GreaterThan<2, 1>>(true); 9 | expectType<GreaterThan<10, 2>>(true); 10 | expectType<GreaterThan<10, -2>>(true); 11 | expectType<GreaterThan<2, 2>>(false); 12 | expectType<GreaterThan<-2, -2>>(false); 13 | expectType<GreaterThan<-2, -3>>(true); 14 | expectType<GreaterThan<-2, number>>(never); 15 | 16 | // === unions === 17 | expectType<GreaterThan<100 | 200, 50>>(true); 18 | expectType<GreaterThan<25, -100 | -15 | 2 | 21>>(true); 19 | expectType<GreaterThan<10 | 15, -5 | 5>>(true); 20 | 21 | expectType<GreaterThan<10, 50 | 100>>(false); 22 | expectType<GreaterThan<50 | 25 | 0 | -16, 100>>(false); 23 | expectType<GreaterThan<1 | 2 | 3, 3 | 4>>(false); 24 | 25 | expectType<GreaterThan<-10, -90 | 90>>({} as boolean); 26 | expectType<GreaterThan<-16 | 16, 0>>({} as boolean); 27 | expectType<GreaterThan<-4 | 45, 20 | 30>>({} as boolean); 28 | expectType<GreaterThan<1 | -1 | 3, 0 | 2>>({} as boolean); 29 | 30 | expectType<GreaterThan<PositiveInfinity, -999>>(true); 31 | expectType<GreaterThan<PositiveInfinity, 999>>(true); 32 | expectType<GreaterThan<999, PositiveInfinity>>(false); 33 | expectType<GreaterThan<999, NegativeInfinity>>(true); 34 | expectType<GreaterThan<-999, NegativeInfinity>>(true); 35 | expectType<GreaterThan<PositiveInfinity, PositiveInfinity>>(false); 36 | expectType<GreaterThan<NegativeInfinity, NegativeInfinity>>(false); 37 | expectType<GreaterThan<PositiveInfinity, NegativeInfinity>>(true); 38 | -------------------------------------------------------------------------------- /test-d/has-optional-keys.ts: -------------------------------------------------------------------------------- 1 | import {expectType} from 'tsd'; 2 | import type {HasOptionalKeys} from '../index.d.ts'; 3 | 4 | type TestType1 = { 5 | a: string; 6 | b?: boolean; 7 | }; 8 | 9 | type TestType2 = { 10 | a?: string; 11 | b?: boolean; 12 | }; 13 | 14 | type TestType3 = { 15 | a: string; 16 | b: boolean; 17 | }; 18 | 19 | type HasOptionalKeys1 = HasOptionalKeys<TestType1>; 20 | type HasOptionalKeys2 = HasOptionalKeys<TestType2>; 21 | type HasOptionalKeys3 = HasOptionalKeys<TestType3>; 22 | 23 | declare const test1: HasOptionalKeys1; 24 | declare const test2: HasOptionalKeys2; 25 | declare const test3: HasOptionalKeys3; 26 | 27 | expectType<true>(test1); 28 | expectType<true>(test2); 29 | expectType<false>(test3); 30 | -------------------------------------------------------------------------------- /test-d/has-readonly-keys.ts: -------------------------------------------------------------------------------- 1 | import {expectType} from 'tsd'; 2 | import type {HasReadonlyKeys} from '../index.d.ts'; 3 | 4 | type TestType1 = { 5 | a: string; 6 | readonly b: boolean; 7 | }; 8 | 9 | type TestType2 = { 10 | readonly a: string; 11 | readonly b: boolean; 12 | }; 13 | 14 | type TestType3 = { 15 | a: string; 16 | b: boolean; 17 | }; 18 | 19 | type HasReadonlyKeys1 = HasReadonlyKeys<TestType1>; 20 | type HasReadonlyKeys2 = HasReadonlyKeys<TestType2>; 21 | type HasReadonlyKeys3 = HasReadonlyKeys<TestType3>; 22 | 23 | declare const test1: HasReadonlyKeys1; 24 | declare const test2: HasReadonlyKeys2; 25 | declare const test3: HasReadonlyKeys3; 26 | 27 | expectType<true>(test1); 28 | expectType<true>(test2); 29 | expectType<false>(test3); 30 | -------------------------------------------------------------------------------- /test-d/has-required-keys.ts: -------------------------------------------------------------------------------- 1 | import {expectType} from 'tsd'; 2 | import type {HasRequiredKeys} from '../index.d.ts'; 3 | 4 | type TestType1 = { 5 | a: string; 6 | b?: boolean; 7 | }; 8 | 9 | type TestType2 = { 10 | a?: string; 11 | b?: boolean; 12 | }; 13 | 14 | type TestType3 = { 15 | a: string; 16 | b: boolean; 17 | }; 18 | 19 | type HasRequiredKeys1 = HasRequiredKeys<TestType1>; 20 | type HasRequiredKeys2 = HasRequiredKeys<TestType2>; 21 | type HasRequiredKeys3 = HasRequiredKeys<TestType3>; 22 | 23 | declare const test1: HasRequiredKeys1; 24 | declare const test2: HasRequiredKeys2; 25 | declare const test3: HasRequiredKeys3; 26 | 27 | expectType<true>(test1); 28 | expectType<false>(test2); 29 | expectType<true>(test3); 30 | -------------------------------------------------------------------------------- /test-d/has-writable-keys.ts: -------------------------------------------------------------------------------- 1 | import {expectType} from 'tsd'; 2 | import type {HasWritableKeys} from '../index.d.ts'; 3 | 4 | type TestType1 = { 5 | a: string; 6 | readonly b: boolean; 7 | }; 8 | 9 | type TestType2 = { 10 | readonly a: string; 11 | readonly b: boolean; 12 | }; 13 | 14 | type TestType3 = { 15 | a: string; 16 | b: boolean; 17 | }; 18 | 19 | type HasWritableKeys1 = HasWritableKeys<TestType1>; 20 | type HasWritableKeys2 = HasWritableKeys<TestType2>; 21 | type HasWritableKeys3 = HasWritableKeys<TestType3>; 22 | 23 | declare const test1: HasWritableKeys1; 24 | declare const test2: HasWritableKeys2; 25 | declare const test3: HasWritableKeys3; 26 | 27 | expectType<true>(test1); 28 | expectType<false>(test2); 29 | expectType<true>(test3); 30 | -------------------------------------------------------------------------------- /test-d/if-any.ts: -------------------------------------------------------------------------------- 1 | import {expectType} from 'tsd'; 2 | import type {IfAny} from '../index.d.ts'; 3 | 4 | declare const _any: any; 5 | 6 | // `IfAny` should return `true`/`false` if only `T` is specified 7 | expectType<IfAny<any>>(true); 8 | expectType<IfAny<string>>(false); 9 | expectType<IfAny<any, 'T', 'F'>>('T'); 10 | expectType<IfAny<string, 'T', 'F'>>('F'); 11 | 12 | // Missing generic parameter 13 | // @ts-expect-error 14 | type A = IfAny; 15 | -------------------------------------------------------------------------------- /test-d/if-never.ts: -------------------------------------------------------------------------------- 1 | import {expectType} from 'tsd'; 2 | import type {IfNever} from '../index.d.ts'; 3 | 4 | // `IfNever` should return `true`/`false` if only `T` is specified 5 | expectType<IfNever<never>>(true); 6 | expectType<IfNever<string>>(false); 7 | expectType<IfNever<never, 'T', 'F'>>('T'); 8 | expectType<IfNever<string, 'T', 'F'>>('F'); 9 | 10 | // Missing generic parameter 11 | // @ts-expect-error 12 | type A = IfNever; 13 | -------------------------------------------------------------------------------- /test-d/if-unknown.ts: -------------------------------------------------------------------------------- 1 | import {expectType} from 'tsd'; 2 | import type {IfUnknown} from '../index.d.ts'; 3 | 4 | // `IfUnknown` should return `true`/`false` if only `T` is specified 5 | expectType<IfUnknown<unknown>>(true); 6 | expectType<IfUnknown<string>>(false); 7 | expectType<IfUnknown<unknown, 'T', 'F'>>('T'); 8 | expectType<IfUnknown<string, 'T', 'F'>>('F'); 9 | 10 | // Missing generic parameter 11 | // @ts-expect-error 12 | type A = IfUnknown; 13 | -------------------------------------------------------------------------------- /test-d/if.ts: -------------------------------------------------------------------------------- 1 | import {expectType} from 'tsd'; 2 | import type {If} from '../source/if.d.ts'; 3 | 4 | expectType<string>({} as If<true, string, number>); 5 | expectType<number>({} as If<false, string, number>); 6 | expectType<string | number>({} as If<boolean, string, number>); 7 | expectType<string | number>({} as If<any, string, number>); 8 | expectType<number>({} as If<never, string, number>); 9 | -------------------------------------------------------------------------------- /test-d/int-closed-range.ts: -------------------------------------------------------------------------------- 1 | import {expectType, expectAssignable} from 'tsd'; 2 | import type {IntClosedRange} from '../source/int-closed-range.d.ts'; 3 | 4 | declare const test: IntClosedRange<0, 5>; 5 | expectType<0 | 1 | 2 | 3 | 4 | 5>(test); 6 | 7 | declare const startTest: IntClosedRange<5, 10>; 8 | expectType<5 | 6 | 7 | 8 | 9 | 10>(startTest); 9 | 10 | declare const stepTest1: IntClosedRange<10, 20, 2>; 11 | expectType<10 | 12 | 14 | 16 | 18 | 20>(stepTest1); 12 | 13 | // Test for step > end - start 14 | declare const stepTest2: IntClosedRange<10, 20, 100>; 15 | expectType<10>(stepTest2); 16 | 17 | type Int0_998 = IntClosedRange<0, 998>; 18 | declare const maxNumberTest: Int0_998; 19 | expectAssignable<number>(maxNumberTest); 20 | expectAssignable<Int0_998>(998); 21 | -------------------------------------------------------------------------------- /test-d/int-range.ts: -------------------------------------------------------------------------------- 1 | import {expectType, expectAssignable} from 'tsd'; 2 | import type {IntRange} from '../source/int-range.d.ts'; 3 | 4 | declare const test: IntRange<0, 5>; 5 | expectType<0 | 1 | 2 | 3 | 4>(test); 6 | 7 | declare const startTest: IntRange<5, 10>; 8 | expectType<5 | 6 | 7 | 8 | 9>(startTest); 9 | 10 | declare const stepTest1: IntRange<10, 20, 2>; 11 | expectType<10 | 12 | 14 | 16 | 18>(stepTest1); 12 | 13 | // Test for step > end - start 14 | declare const stepTest2: IntRange<10, 20, 100>; 15 | expectType<10>(stepTest2); 16 | 17 | declare const maxNumberTest: IntRange<0, 999>; 18 | expectAssignable<number>(maxNumberTest); 19 | 20 | // Not yet supported. 21 | // declare const negative: IntRange<-1, 1>; 22 | // expectType<-1 | 0 | 1>(negative); 23 | 24 | // declare const negative2: IntRange<1, -1>; 25 | // expectType<-1 | 0 | 1>(negative2); 26 | -------------------------------------------------------------------------------- /test-d/internal/build-tuple.ts: -------------------------------------------------------------------------------- 1 | import {expectType} from 'tsd'; 2 | import type {BuildTuple} from '../../source/internal/index.d.ts'; 3 | 4 | expectType<BuildTuple<3, null>>([null, null, null]); 5 | expectType<BuildTuple<5, 0>>([0, 0, 0, 0, 0]); 6 | expectType<BuildTuple<0, 0>>([]); 7 | expectType<BuildTuple<2 | 3, 0>>({} as [0, 0] | [0, 0, 0]); 8 | expectType<BuildTuple<number, 0>>({} as Array<0>); 9 | -------------------------------------------------------------------------------- /test-d/internal/has-multiple-call-signatures.ts: -------------------------------------------------------------------------------- 1 | import {expectType} from 'tsd'; 2 | import type {HasMultipleCallSignatures} from '../../source/internal/index.d.ts'; 3 | 4 | type Overloaded = { 5 | (foo: number): string; 6 | (foo: string, bar: number): number; 7 | }; 8 | 9 | type Overloaded2 = { 10 | (foo: number | undefined): string; 11 | // eslint-disable-next-line @typescript-eslint/unified-signatures 12 | (foo: number): string; 13 | }; 14 | 15 | type Namespace = { 16 | (foo: number): string; 17 | baz: boolean[]; 18 | }; 19 | 20 | expectType<true>({} as HasMultipleCallSignatures<Overloaded>); 21 | expectType<true>({} as HasMultipleCallSignatures<Overloaded2>); 22 | expectType<false>({} as HasMultipleCallSignatures<Namespace>); 23 | -------------------------------------------------------------------------------- /test-d/internal/if-not-any-or-never.ts: -------------------------------------------------------------------------------- 1 | import {expectType} from 'tsd'; 2 | import type {IfNotAnyOrNever} from '../../source/internal/index.d.ts'; 3 | 4 | expectType<any>({} as IfNotAnyOrNever<any, string>); 5 | expectType<never>({} as IfNotAnyOrNever<never, string>); 6 | expectType<number>({} as IfNotAnyOrNever<any, string, number>); 7 | expectType<number>({} as IfNotAnyOrNever<any, string, number, boolean>); 8 | expectType<never>({} as IfNotAnyOrNever<never, string, number>); 9 | expectType<boolean>({} as IfNotAnyOrNever<never, string, number, boolean>); 10 | expectType<number>({} as IfNotAnyOrNever<string, number>); 11 | expectType<number>({} as IfNotAnyOrNever<string | number, number, boolean>); 12 | expectType<number>({} as IfNotAnyOrNever<object, number, boolean, string>); 13 | -------------------------------------------------------------------------------- /test-d/internal/is-array-readonly.ts: -------------------------------------------------------------------------------- 1 | import {expectType} from 'tsd'; 2 | import type {IsArrayReadonly} from '../../source/internal/index.d.ts'; 3 | 4 | // Non-readonly arrays 5 | expectType<IsArrayReadonly<[]>>(false); 6 | expectType<IsArrayReadonly<number[]>>(false); 7 | expectType<IsArrayReadonly<[string, number?, ...string[]]>>(false); 8 | expectType<IsArrayReadonly<[x: number, y: number, z?: number]>>(false); 9 | expectType<IsArrayReadonly<[...string[], number, string]>>(false); 10 | 11 | // Readonly arrays 12 | expectType<IsArrayReadonly<readonly []>>(true); 13 | expectType<IsArrayReadonly<readonly number[]>>(true); 14 | expectType<IsArrayReadonly<readonly [string, number?, ...string[]]>>(true); 15 | expectType<IsArrayReadonly<readonly [x: number, y: number, z?: number]>>(true); 16 | expectType<IsArrayReadonly<readonly [...string[], number, string]>>(true); 17 | 18 | // Union 19 | expectType<IsArrayReadonly<[] | readonly []>>({} as boolean); 20 | expectType<IsArrayReadonly<[string, number] | readonly [string, number, ...string[]]>>({} as boolean); 21 | expectType<IsArrayReadonly<[] | [string, number]>>(false); 22 | expectType<IsArrayReadonly<readonly [] | readonly [string, number]>>(true); 23 | 24 | // Boundary types 25 | expectType<IsArrayReadonly<any>>({} as boolean); 26 | expectType<IsArrayReadonly<never>>(false); 27 | -------------------------------------------------------------------------------- /test-d/internal/is-not-false.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-duplicate-type-constituents */ 2 | import {expectType} from 'tsd'; 3 | import type {IsNotFalse} from '../../source/internal/index.d.ts'; 4 | 5 | expectType<IsNotFalse<true>>(true); 6 | expectType<IsNotFalse<boolean>>(true); 7 | expectType<IsNotFalse<true | false>>(true); 8 | expectType<IsNotFalse<true | false | false | false>>(true); 9 | expectType<IsNotFalse<false>>(false); 10 | expectType<IsNotFalse<false | false>>(false); 11 | expectType<IsNotFalse<false | false | false | false>>(false); 12 | -------------------------------------------------------------------------------- /test-d/internal/is-number-like.ts: -------------------------------------------------------------------------------- 1 | import {expectType} from 'tsd'; 2 | import type {IsNumberLike} from '../../source/internal/numeric.d.ts'; 3 | 4 | expectType<IsNumberLike<'1'>>(true); 5 | expectType<IsNumberLike<1>>(true); 6 | expectType<IsNumberLike<'-1.1'>>(true); 7 | expectType<IsNumberLike<-1.1>>(true); 8 | expectType<IsNumberLike<'foo'>>(false); 9 | -------------------------------------------------------------------------------- /test-d/internal/is-numeric.ts: -------------------------------------------------------------------------------- 1 | import {expectType} from 'tsd'; 2 | import type {IsNumeric} from '../../source/internal/index.d.ts'; 3 | 4 | expectType<IsNumeric<''>>(false); 5 | expectType<IsNumeric<'0'>>(true); 6 | expectType<IsNumeric<'1'>>(true); 7 | expectType<IsNumeric<'-1'>>(true); 8 | expectType<IsNumeric<'123'>>(true); 9 | expectType<IsNumeric<'1e2'>>(true); 10 | expectType<IsNumeric<'1.23'>>(true); 11 | expectType<IsNumeric<'123.456'>>(true); 12 | expectType<IsNumeric<'1.23e4'>>(true); 13 | expectType<IsNumeric<'1.23e-4'>>(true); 14 | expectType<IsNumeric<' '>>(false); 15 | expectType<IsNumeric<'\n'>>(false); 16 | expectType<IsNumeric<'\u{9}'>>(false); 17 | expectType<IsNumeric<' 1.2'>>(false); 18 | expectType<IsNumeric<'1 2'>>(false); 19 | expectType<IsNumeric<'1_200'>>(false); 20 | expectType<IsNumeric<' 1 '>>(false); 21 | -------------------------------------------------------------------------------- /test-d/internal/is-whitespace.ts: -------------------------------------------------------------------------------- 1 | import {expectType} from 'tsd'; 2 | import type {IsWhitespace} from '../../source/internal/index.d.ts'; 3 | 4 | expectType<IsWhitespace<''>>(false); 5 | expectType<IsWhitespace<' '>>(true); 6 | expectType<IsWhitespace<'\n'>>(true); 7 | expectType<IsWhitespace<'\u{9}'>>(true); 8 | expectType<IsWhitespace<'a'>>(false); 9 | expectType<IsWhitespace<'a '>>(false); 10 | expectType<IsWhitespace<' '>>(true); 11 | expectType<IsWhitespace<' \t '>>(true); 12 | -------------------------------------------------------------------------------- /test-d/internal/not.ts: -------------------------------------------------------------------------------- 1 | import {expectType} from 'tsd'; 2 | import type {Not} from '../../source/internal/index.d.ts'; 3 | 4 | expectType<Not<true>>(false); 5 | expectType<Not<false>>(true); 6 | expectType<Not<boolean>>({} as boolean); 7 | -------------------------------------------------------------------------------- /test-d/internal/number-absolute.ts: -------------------------------------------------------------------------------- 1 | import {expectType} from 'tsd'; 2 | import type {NumberAbsolute} from '../../source/internal/index.d.ts'; 3 | import type {NegativeInfinity, PositiveInfinity} from '../../source/numeric.d.ts'; 4 | 5 | expectType<NumberAbsolute<3>>(3); 6 | expectType<NumberAbsolute<-3>>(3); 7 | expectType<NumberAbsolute<0>>(0); 8 | expectType<NumberAbsolute<-0>>(0); 9 | expectType<NumberAbsolute<NegativeInfinity>>(null! as PositiveInfinity); 10 | expectType<NumberAbsolute<PositiveInfinity>>(null! as PositiveInfinity); 11 | -------------------------------------------------------------------------------- /test-d/internal/object-value.ts: -------------------------------------------------------------------------------- 1 | import {expectType} from 'tsd'; 2 | import type {ObjectValue} from '../../source/internal/index.d.ts'; 3 | 4 | type ObjectT = { 5 | string: string; 6 | 0: number; 7 | '1': number; 8 | }; 9 | 10 | declare const normal: ObjectValue<ObjectT, 'string'>; 11 | expectType<string>(normal); 12 | 13 | declare const test0: ObjectValue<ObjectT, 0>; 14 | expectType<number>(test0); 15 | declare const teststring0: ObjectValue<ObjectT, '0'>; 16 | expectType<number>(teststring0); 17 | declare const test1: ObjectValue<ObjectT, 1>; 18 | expectType<number>(test1); 19 | declare const teststring1: ObjectValue<ObjectT, '1'>; 20 | expectType<number>(teststring1); 21 | -------------------------------------------------------------------------------- /test-d/internal/readonly-keys-of-union.ts: -------------------------------------------------------------------------------- 1 | import {expectType} from 'tsd'; 2 | import type {ReadonlyKeysOfUnion} from '../../source/internal/index.d.ts'; 3 | 4 | declare const test1: ReadonlyKeysOfUnion<{readonly a: 1; b: 2}>; 5 | expectType<'a'>(test1); 6 | 7 | declare const test2: ReadonlyKeysOfUnion<{readonly a: 1; b?: 2} | {readonly c?: 3; d: 4}>; 8 | expectType<'a' | 'c'>(test2); 9 | 10 | declare const test3: ReadonlyKeysOfUnion<{readonly a: 1; b?: 2} | {readonly c?: 3; d: 4} | {readonly c: 5} | {d: 6}>; 11 | expectType<'a' | 'c'>(test3); 12 | 13 | // Returns `never` if there's no readonly key 14 | declare const test4: ReadonlyKeysOfUnion<{a: 1; b?: 2} | {c?: 3; d: 4}>; 15 | expectType<never>(test4); 16 | 17 | // Works with index signatures 18 | declare const test5: ReadonlyKeysOfUnion<{readonly [x: string]: number; a: 1} | {readonly [x: symbol]: number; a: 2}>; 19 | expectType<string | number | symbol>(test5); 20 | 21 | // Works with arrays 22 | declare const test7: ReadonlyKeysOfUnion<readonly string[] | readonly [number, number]>; 23 | expectType<number | typeof Symbol.unscopables | '0' | '1' | 'length'>(test7); 24 | 25 | // Works with functions 26 | declare const test8: ReadonlyKeysOfUnion<(() => void) | {(): void; readonly a: 1}>; 27 | expectType<'a'>(test8); 28 | -------------------------------------------------------------------------------- /test-d/internal/require-none.ts: -------------------------------------------------------------------------------- 1 | import {expectAssignable, expectNotAssignable} from 'tsd'; 2 | import type {RequireNone} from '../../source/internal/index.d.ts'; 3 | 4 | type NoneAllowed = RequireNone<'foo' | 'bar'>; 5 | 6 | expectAssignable<NoneAllowed>({}); 7 | expectNotAssignable<NoneAllowed>({foo: 'foo'}); 8 | expectNotAssignable<NoneAllowed>({bar: 'bar'}); 9 | expectNotAssignable<NoneAllowed>({foo: 'foo', bar: 'bar'}); 10 | 11 | type SomeAllowed = Record<'bar', string> & RequireNone<'foo'>; 12 | 13 | expectAssignable<SomeAllowed>({bar: 'bar'}); 14 | expectNotAssignable<SomeAllowed>({foo: 'foo'}); 15 | expectNotAssignable<SomeAllowed>({foo: 'foo', bar: 'bar'}); 16 | -------------------------------------------------------------------------------- /test-d/internal/tuple-max.ts: -------------------------------------------------------------------------------- 1 | import {expectType} from 'tsd'; 2 | import type {TupleMax} from '../../source/internal/index.d.ts'; 3 | import type {PositiveInfinity} from '../../source/numeric.d.ts'; 4 | 5 | expectType<TupleMax<[1, 2, 5, 3, 7, -9, -5, 0]>>(7); 6 | expectType<TupleMax<[1, 2, 5, 3, 7, -9, -5, 0, PositiveInfinity]>>(null! as PositiveInfinity); 7 | expectType<TupleMax<[1, 1, 1, 1, 1, 1]>>(1); 8 | expectType<TupleMax<[-1, -2, -5]>>(-1); 9 | expectType<TupleMax<[10, 2]>>(10); 10 | -------------------------------------------------------------------------------- /test-d/internal/tuple-min.ts: -------------------------------------------------------------------------------- 1 | import {expectType} from 'tsd'; 2 | import type {TupleMin} from '../../source/internal/index.d.ts'; 3 | import type {NegativeInfinity, PositiveInfinity} from '../../source/numeric.d.ts'; 4 | 5 | declare const never: never; 6 | 7 | expectType<TupleMin<[1, 2, 5, 3, 7, -9, -5, 0]>>(-9); 8 | expectType<TupleMin<[1, 2, 5, 3, 7, -9, -5, 0, PositiveInfinity, NegativeInfinity]>>(null! as NegativeInfinity); 9 | expectType<TupleMin<[1, 1, 1, 1, 1, 1]>>(1); 10 | expectType<TupleMin<[-1, -2, -5]>>(-5); 11 | expectType<TupleMin<[-1, -2, number, -5]>>(never); 12 | -------------------------------------------------------------------------------- /test-d/invariant-of.ts: -------------------------------------------------------------------------------- 1 | import {expectAssignable, expectNotAssignable} from 'tsd'; 2 | import type {InvariantOf} from '../index.d.ts'; 3 | 4 | type FooBar = InvariantOf<{ 5 | foo: number; 6 | bar: string; 7 | }>; 8 | 9 | type FooBarBaz = InvariantOf<{ 10 | foo: number; 11 | bar: string; 12 | baz: boolean; 13 | }>; 14 | 15 | // We make an explicit cast so we can test the value. 16 | const fooBar: FooBar = {foo: 123, bar: 'hello'} as FooBar; // eslint-disable-line @typescript-eslint/consistent-type-assertions 17 | const fooBarBaz: FooBarBaz = {foo: 123, bar: 'hello', baz: true} as FooBarBaz; // eslint-disable-line @typescript-eslint/consistent-type-assertions 18 | 19 | // The invariantOf<Type> is assignable to Type. 20 | expectAssignable<{ 21 | foo: number; 22 | bar: string; 23 | }>(fooBar); 24 | 25 | expectNotAssignable<FooBarBaz>(fooBar); // Invariance does not accept supertypes. 26 | expectNotAssignable<FooBar>(fooBarBaz); // Invariance does not accept subtypes. 27 | -------------------------------------------------------------------------------- /test-d/is-any.ts: -------------------------------------------------------------------------------- 1 | import {expectType} from 'tsd'; 2 | import type {IsAny} from '../index.d.ts'; 3 | 4 | declare const anything: any; 5 | declare const something = 'something'; 6 | 7 | // `IsAny` should only be true for `any` 8 | expectType<IsAny<any>>(true); 9 | expectType<IsAny<typeof anything>>(true); 10 | expectType<IsAny<string>>(false); 11 | expectType<IsAny<typeof something>>(false); 12 | expectType<IsAny<never>>(false); 13 | expectType<IsAny<unknown>>(false); 14 | expectType<IsAny<null>>(false); 15 | expectType<IsAny<undefined>>(false); 16 | expectType<IsAny<void>>(false); 17 | 18 | // Missing generic parameter 19 | // @ts-expect-error 20 | type A = IsAny; 21 | 22 | // Verify that are no circular reference issues 23 | // https://github.com/sindresorhus/type-fest/issues/846 24 | type OnlyAny<T extends IsAny<T> extends true ? any : never> = T; 25 | type B = OnlyAny<any>; 26 | // @ts-expect-error 27 | type C = OnlyAny<string>; 28 | -------------------------------------------------------------------------------- /test-d/is-equal.ts: -------------------------------------------------------------------------------- 1 | import {expectType} from 'tsd'; 2 | import type {IsEqual} from '../index.d.ts'; 3 | 4 | const notEqualNumberAndString: IsEqual<number, string> = false; 5 | expectType<false>(notEqualNumberAndString); 6 | 7 | const equalNumbers: IsEqual<1, 1> = true; 8 | expectType<true>(equalNumbers); 9 | 10 | const notEqualAnyAndNumber: IsEqual<any, number> = false; 11 | expectType<false>(notEqualAnyAndNumber); 12 | 13 | const notEqualUnionAndNumber: IsEqual<1 | 2, 1> = false; 14 | expectType<false>(notEqualUnionAndNumber); 15 | 16 | const notEqualAnyAndNever: IsEqual<any, never> = false; 17 | expectType<false>(notEqualAnyAndNever); 18 | 19 | const notEqualArrayOfAnyAndArrayOfNever: IsEqual<[any], [never]> = false; 20 | expectType<false>(notEqualArrayOfAnyAndArrayOfNever); 21 | 22 | // Missing all generic parameters. 23 | // @ts-expect-error 24 | type A = IsEqual; 25 | 26 | // Missing `Y` generic parameter. 27 | // @ts-expect-error 28 | type B = IsEqual<number>; 29 | 30 | // Test for issue https://github.com/sindresorhus/type-fest/issues/537 31 | type UnionType = IsEqual<{a: 1} & {a: 1}, {a: 1}>; // eslint-disable-line @typescript-eslint/no-duplicate-type-constituents 32 | expectType<UnionType>(true); 33 | 34 | type IntersectionType = IsEqual<{a: 1} | {a: 1}, {a: 1}>; // eslint-disable-line @typescript-eslint/no-duplicate-type-constituents 35 | expectType<IntersectionType>(true); 36 | -------------------------------------------------------------------------------- /test-d/is-float.ts: -------------------------------------------------------------------------------- 1 | import {expectType} from 'tsd'; 2 | import type {IsFloat, PositiveInfinity} from '../index.d.ts'; 3 | 4 | declare const x: unknown; 5 | 6 | expectType<true>(x as IsFloat<1.5>); 7 | expectType<true>(x as IsFloat<999_999_999_999_999.9>); 8 | expectType<true>(x as IsFloat<0.000_000_1>); 9 | expectType<true>(x as IsFloat<-1e-7>); 10 | 11 | expectType<false>(x as IsFloat<0>); 12 | expectType<false>(x as IsFloat<1>); 13 | expectType<false>(x as IsFloat<1.0>); // eslint-disable-line unicorn/no-zero-fractions 14 | expectType<false>(x as IsFloat<-1>); 15 | expectType<false>(x as IsFloat<number>); 16 | expectType<false>(x as IsFloat<0o10>); 17 | expectType<false>(x as IsFloat<1n>); 18 | expectType<false>(x as IsFloat<0n>); 19 | expectType<false>(x as IsFloat<0b10>); 20 | expectType<false>(x as IsFloat<0x10>); 21 | expectType<false>(x as IsFloat<1e+100>); 22 | expectType<false>(x as IsFloat<1.23e+21>); 23 | expectType<false>(x as IsFloat<-1.23e+21>); 24 | expectType<false>(x as IsFloat<'1.23'>); 25 | expectType<false>(x as IsFloat<PositiveInfinity>); 26 | -------------------------------------------------------------------------------- /test-d/is-integer.ts: -------------------------------------------------------------------------------- 1 | import {expectType} from 'tsd'; 2 | import type {IsInteger, PositiveInfinity} from '../index.d.ts'; 3 | 4 | declare const x: unknown; 5 | 6 | expectType<true>(x as IsInteger<0>); 7 | expectType<true>(x as IsInteger<1>); 8 | expectType<true>(x as IsInteger<1.0>); // eslint-disable-line unicorn/no-zero-fractions 9 | expectType<true>(x as IsInteger<-1>); 10 | expectType<true>(x as IsInteger<0o10>); 11 | expectType<true>(x as IsInteger<1n>); 12 | expectType<true>(x as IsInteger<0n>); 13 | expectType<true>(x as IsInteger<0b10>); 14 | expectType<true>(x as IsInteger<0x10>); 15 | expectType<true>(x as IsInteger<1e+100>); 16 | expectType<true>(x as IsInteger<1.23e+21>); 17 | expectType<true>(x as IsInteger<-1.23e+21>); 18 | 19 | expectType<false>(x as IsInteger<1.5>); 20 | expectType<false>(x as IsInteger<-1.5>); 21 | expectType<false>(x as IsInteger<number>); 22 | expectType<false>(x as IsInteger<PositiveInfinity>); 23 | expectType<false>(x as IsInteger<0.000_000_1>); 24 | expectType<false>(x as IsInteger<-1e-7>); 25 | -------------------------------------------------------------------------------- /test-d/is-never.ts: -------------------------------------------------------------------------------- 1 | import {expectType} from 'tsd'; 2 | import type {IsNever} from '../index.d.ts'; 3 | 4 | declare const _never: never; 5 | declare const something = 'something'; 6 | 7 | // `IsNever` should only be true for `any` 8 | expectType<IsNever<never>>(true); 9 | expectType<IsNever<typeof _never>>(true); 10 | expectType<IsNever<string>>(false); 11 | expectType<IsNever<typeof something>>(false); 12 | expectType<IsNever<any>>(false); 13 | expectType<IsNever<unknown>>(false); 14 | expectType<IsNever<null>>(false); 15 | expectType<IsNever<undefined>>(false); 16 | expectType<IsNever<void>>(false); 17 | 18 | // Missing generic parameter 19 | // @ts-expect-error 20 | type A = IsNever; 21 | -------------------------------------------------------------------------------- /test-d/is-null.ts: -------------------------------------------------------------------------------- 1 | import {expectType} from 'tsd'; 2 | import type {IsNull} from '../source/is-null.d.ts'; 3 | 4 | // https://www.typescriptlang.org/docs/handbook/type-compatibility.html 5 | expectType<IsNull<null>>(true); 6 | expectType<IsNull<any>>(true); 7 | expectType<IsNull<never>>(true); 8 | expectType<IsNull<undefined>>(false); // Depends on `strictNullChecks` 9 | expectType<IsNull<unknown>>(false); 10 | expectType<IsNull<void>>(false); 11 | expectType<IsNull<{}>>(false); 12 | -------------------------------------------------------------------------------- /test-d/is-nullable.ts: -------------------------------------------------------------------------------- 1 | import {expectType} from 'tsd'; 2 | import type {IsNullable} from '../source/is-nullable.d.ts'; 3 | 4 | expectType<IsNullable<any>>(true); 5 | expectType<IsNullable<null>>(true); 6 | expectType<IsNullable<null | undefined>>(true); 7 | expectType<IsNullable<string | null>>(true); 8 | expectType<IsNullable<string | null | undefined>>(true); 9 | 10 | expectType<IsNullable<string>>(false); 11 | expectType<IsNullable<string | undefined>>(false); 12 | 13 | expectType<IsNullable<void>>(false); 14 | expectType<IsNullable<undefined>>(false); 15 | expectType<IsNullable<never>>(false); 16 | expectType<IsNullable<unknown>>(false); 17 | expectType<IsNullable<() => void>>(false); 18 | expectType<IsNullable<string & null>>(false); 19 | -------------------------------------------------------------------------------- /test-d/is-optional.ts: -------------------------------------------------------------------------------- 1 | import {expectType} from 'tsd'; 2 | import type {IsOptional} from '../source/is-optional.d.ts'; 3 | 4 | expectType<IsOptional<any>>(true); 5 | expectType<IsOptional<undefined>>(true); 6 | expectType<IsOptional<null | undefined>>(true); 7 | expectType<IsOptional<string | undefined>>(true); 8 | expectType<IsOptional<string | null | undefined>>(true); 9 | 10 | expectType<IsOptional<string>>(false); 11 | expectType<IsOptional<string | null>>(false); 12 | 13 | expectType<IsOptional<void>>(false); 14 | expectType<IsOptional<null>>(false); 15 | expectType<IsOptional<never>>(false); 16 | expectType<IsOptional<unknown>>(false); 17 | expectType<IsOptional<() => void>>(false); 18 | expectType<IsOptional<string & undefined>>(false); 19 | -------------------------------------------------------------------------------- /test-d/is-union.ts: -------------------------------------------------------------------------------- 1 | import {expectType} from 'tsd'; 2 | import type {IsUnion} from '../index.d.ts'; 3 | 4 | expectType<IsUnion<1>>(false); 5 | expectType<IsUnion<true>>(false); 6 | expectType<IsUnion<'foo'>>(false); 7 | expectType<IsUnion<[]>>(false); 8 | expectType<IsUnion<{}>>(false); 9 | expectType<IsUnion<1 & {}>>(false); 10 | expectType<IsUnion<never>>(false); 11 | expectType<IsUnion<unknown>>(false); 12 | expectType<IsUnion<any>>(false); 13 | 14 | expectType<IsUnion<1 | 2>>(true); 15 | expectType<IsUnion<'foo' | 'bar'>>(true); 16 | expectType<IsUnion<'foo' | 'bar' | 1>>(true); 17 | expectType<IsUnion<'foo' | 1>>(true); 18 | expectType<IsUnion<[] | {}>>(true); 19 | -------------------------------------------------------------------------------- /test-d/is-unknown.ts: -------------------------------------------------------------------------------- 1 | import {expectType} from 'tsd'; 2 | import type {IsUnknown} from '../index.d.ts'; 3 | 4 | declare const _unknown: unknown; 5 | declare const something = 'something'; 6 | 7 | // `IsUnknown` should only be true for `any` 8 | expectType<IsUnknown<unknown>>(true); 9 | expectType<IsUnknown<typeof _unknown>>(true); 10 | expectType<IsUnknown<string>>(false); 11 | expectType<IsUnknown<typeof something>>(false); 12 | expectType<IsUnknown<any>>(false); 13 | expectType<IsUnknown<never>>(false); 14 | expectType<IsUnknown<null>>(false); 15 | expectType<IsUnknown<undefined>>(false); 16 | expectType<IsUnknown<void>>(false); 17 | 18 | // Missing generic parameter 19 | // @ts-expect-error 20 | type A = IsUnknown; 21 | -------------------------------------------------------------------------------- /test-d/iterable-element.ts: -------------------------------------------------------------------------------- 1 | import {expectAssignable, expectType} from 'tsd'; 2 | import type {IterableElement} from '../index.d.ts'; 3 | 4 | declare const iterableElement: IterableElement<ReturnType<typeof secretGenerator>>; 5 | expectType<1 | 'two'>(iterableElement); 6 | 7 | declare const iterableElementAsync: IterableElement<ReturnType<typeof secretGeneratorAsync>>; 8 | expectType<true | Date>(iterableElementAsync); 9 | 10 | function * secretGenerator() { 11 | yield 1; 12 | yield 'two'; 13 | } 14 | 15 | async function * secretGeneratorAsync() { 16 | yield true; 17 | yield new Date(); 18 | } 19 | 20 | const fruits = new Set(['🍎', '🍌', '🍉'] as const); 21 | 22 | type Fruit = IterableElement<typeof fruits>; 23 | 24 | expectAssignable<Fruit>('🍎'); 25 | expectAssignable<Fruit>('🍌'); 26 | expectAssignable<Fruit>('🍉'); 27 | 28 | type VegetableSet = Set<'🥦' | '🥕' | '🌶'>; 29 | type Vegetable = IterableElement<VegetableSet>; 30 | 31 | expectAssignable<Vegetable>('🥦'); 32 | expectAssignable<Vegetable>('🥕'); 33 | expectAssignable<Vegetable>('🌶'); 34 | 35 | type UserRolesSet = ReadonlySet<'regular' | 'contributor' | 'maintainer'>; 36 | type UserRole = IterableElement<UserRolesSet>; 37 | 38 | expectAssignable<UserRole>('regular'); 39 | expectAssignable<UserRole>('contributor'); 40 | expectAssignable<UserRole>('maintainer'); 41 | -------------------------------------------------------------------------------- /test-d/jsonifiable.ts: -------------------------------------------------------------------------------- 1 | import {expectAssignable, expectNotAssignable} from 'tsd'; 2 | import type {Jsonifiable} from '../index.d.ts'; 3 | 4 | expectAssignable<Jsonifiable>(1); 5 | expectAssignable<Jsonifiable>(''); 6 | expectAssignable<Jsonifiable>(null); 7 | expectAssignable<Jsonifiable>(new Date()); 8 | expectAssignable<Jsonifiable>({a: new Date()}); 9 | expectAssignable<Jsonifiable>([new Date()]); 10 | expectAssignable<Jsonifiable>({a: undefined}); 11 | expectAssignable<Jsonifiable>([1, 2, 3] as const); 12 | expectAssignable<Jsonifiable>({a: new Date()} as const); 13 | expectAssignable<Jsonifiable>({a: {deeply: {nested: {toJsonObject: new Date()}}}}); 14 | expectAssignable<Jsonifiable>({toJSON: () => new Date()}); 15 | expectAssignable<Jsonifiable>({ 16 | toJSON() { 17 | return { 18 | foo: { 19 | toJSON() { 20 | return {bar: 'bar'}; 21 | }, 22 | }, 23 | }; 24 | }, 25 | }); 26 | 27 | expectNotAssignable<Jsonifiable>(undefined); 28 | expectNotAssignable<Jsonifiable>(new Map()); 29 | expectNotAssignable<Jsonifiable>({a: new Map()}); 30 | expectNotAssignable<Jsonifiable>([new Map()]); 31 | -------------------------------------------------------------------------------- /test-d/kebab-cased-properties-deep.ts: -------------------------------------------------------------------------------- 1 | import {expectType} from 'tsd'; 2 | import type {KebabCasedPropertiesDeep} from '../index.d.ts'; 3 | 4 | type FooBar = {helloWorld: {p2p: Array<{addressLine1: string}>}}; 5 | 6 | declare const foo: KebabCasedPropertiesDeep<FooBar>; 7 | expectType<{'hello-world': {p2p: Array<{'address-line1': string}>}}>(foo); 8 | 9 | declare const bar: KebabCasedPropertiesDeep<FooBar, {splitOnNumbers: true}>; 10 | expectType<{'hello-world': {'p-2-p': Array<{'address-line-1': string}>}}>(bar); 11 | 12 | // Verify example 13 | type User = { 14 | userId: number; 15 | userName: string; 16 | date: Date; 17 | regExp: RegExp; 18 | }; 19 | 20 | type UserWithFriends = { 21 | userInfo: User; 22 | userFriends: User[]; 23 | }; 24 | 25 | const result: KebabCasedPropertiesDeep<UserWithFriends> = { 26 | 'user-info': { 27 | 'user-id': 1, 28 | 'user-name': 'Tom', 29 | date: new Date(), 30 | 'reg-exp': /.*/, 31 | }, 32 | 'user-friends': [ 33 | { 34 | 'user-id': 2, 35 | 'user-name': 'Jerry', 36 | date: new Date(), 37 | 'reg-exp': /.*/, 38 | }, 39 | { 40 | 'user-id': 3, 41 | 'user-name': 'Spike', 42 | date: new Date(), 43 | 'reg-exp': /.*/, 44 | }, 45 | ], 46 | }; 47 | expectType<KebabCasedPropertiesDeep<UserWithFriends>>(result); 48 | 49 | expectType<{'foo-bar': unknown}>({} as KebabCasedPropertiesDeep<{foo_bar: unknown}>); 50 | expectType<{'foo-bar': {'bar-baz': unknown}; biz: unknown}>({} as KebabCasedPropertiesDeep<{foo_bar: {bar_baz: unknown}; biz: unknown}>); 51 | 52 | expectType<{'foo-bar': any}>({} as KebabCasedPropertiesDeep<{foo_bar: any}>); 53 | expectType<{'foo-bar': {'bar-baz': any}; biz: any}>({} as KebabCasedPropertiesDeep<{foo_bar: {bar_baz: any}; biz: any}>); 54 | -------------------------------------------------------------------------------- /test-d/kebab-cased-properties.ts: -------------------------------------------------------------------------------- 1 | import {expectType} from 'tsd'; 2 | import type {KebabCasedProperties} from '../index.d.ts'; 3 | 4 | type Foobar = {helloWorld1: {fooBar: string}}; 5 | 6 | declare const foo: KebabCasedProperties<Foobar>; 7 | expectType<{'hello-world1': {fooBar: string}}>(foo); 8 | 9 | declare const bar: KebabCasedProperties<Foobar, {splitOnNumbers: true}>; 10 | expectType<{'hello-world-1': {fooBar: string}}>(bar); 11 | 12 | // Verify example 13 | type User = { 14 | userId: number; 15 | userName: string; 16 | }; 17 | const result: KebabCasedProperties<User> = { 18 | 'user-id': 1, 19 | 'user-name': 'Tom', 20 | }; 21 | expectType<KebabCasedProperties<User>>(result); 22 | -------------------------------------------------------------------------------- /test-d/key-as-string.ts: -------------------------------------------------------------------------------- 1 | import {expectType} from 'tsd'; 2 | import type {KeyAsString} from '../source/key-as-string.d.ts'; 3 | 4 | declare const foo: KeyAsString<{ 5 | 1: number; 6 | stringKey: string; 7 | }>; 8 | 9 | expectType<'1' | 'stringKey'>(foo); 10 | 11 | declare const sym: unique symbol; 12 | expectType<'1' | 'foo'>({} as KeyAsString<{[sym]: unknown; 1: number; foo: string}>); // Ignores symbol keys 13 | 14 | // Index signatures 15 | expectType<string>({} as KeyAsString<{[x: string]: unknown}>); 16 | expectType<`${number}`>({} as KeyAsString<{[x: number]: unknown}>); 17 | expectType<`foo${string}` | `${number}`>({} as KeyAsString<{[x: `foo${string}`]: unknown; [y: number]: unknown}>); 18 | expectType<string>({} as KeyAsString<{[x: string]: unknown; [y: symbol]: unknown}>); 19 | expectType<never>({} as KeyAsString<{[x: symbol]: unknown}>); 20 | 21 | // Unions 22 | expectType<'1'>({} as KeyAsString<{1: number; foo: string} | {1: number; bar: string}>); // Only grabs the common keys, just like `keyof`. 23 | expectType<never>({} as KeyAsString<{foo: string} | {bar: string}>); 24 | 25 | // Boundary cases 26 | expectType<string>({} as KeyAsString<any>); 27 | expectType<string>({} as KeyAsString<never>); 28 | -------------------------------------------------------------------------------- /test-d/last-array-element.ts: -------------------------------------------------------------------------------- 1 | import {expectType} from 'tsd'; 2 | import type {LastArrayElement} from '../index.d.ts'; 3 | 4 | declare function lastOf<V extends readonly unknown[]>(array: V): LastArrayElement<V>; 5 | const array: ['foo', 2, 'bar'] = ['foo', 2, 'bar']; 6 | const mixedArray: ['bar', 'foo', 2] = ['bar', 'foo', 2]; 7 | 8 | expectType<'bar'>(lastOf(array)); 9 | expectType<2>(lastOf(mixedArray)); 10 | expectType<string>(lastOf(['a', 'b', 'c'])); 11 | expectType<string | number>(lastOf(['a', 'b', 1])); 12 | expectType<1>(lastOf(['a', 'b', 1] as const)); 13 | 14 | declare const leadingSpreadTuple: [...string[], object, number]; 15 | expectType<number>(lastOf(leadingSpreadTuple)); 16 | 17 | declare const trailingSpreadTuple1: [string, ...number[]]; 18 | expectType<number | string>(lastOf(trailingSpreadTuple1)); 19 | 20 | declare const trailingSpreadTuple2: [string, boolean, ...number[]]; 21 | expectType<number | boolean>(lastOf(trailingSpreadTuple2)); 22 | 23 | // eslint-disable-next-line @typescript-eslint/array-type 24 | declare const trailingSpreadTuple3: ['foo', true, ...(1 | '2')[]]; 25 | expectType<true | 1 | '2'>(lastOf(trailingSpreadTuple3)); 26 | -------------------------------------------------------------------------------- /test-d/less-than-or-equal.ts: -------------------------------------------------------------------------------- 1 | import {expectType} from 'tsd'; 2 | import type {LessThanOrEqual} from '../index.d.ts'; 3 | import type {NegativeInfinity, PositiveInfinity} from '../source/numeric.d.ts'; 4 | 5 | declare const never: never; 6 | 7 | expectType<LessThanOrEqual<1, 2>>(true); 8 | expectType<LessThanOrEqual<2, 1>>(false); 9 | expectType<LessThanOrEqual<10, 2>>(false); 10 | expectType<LessThanOrEqual<10, -2>>(false); 11 | expectType<LessThanOrEqual<2, 2>>(true); 12 | expectType<LessThanOrEqual<-2, -2>>(true); 13 | expectType<LessThanOrEqual<-2, -3>>(false); 14 | expectType<LessThanOrEqual<-2, number>>(never); 15 | expectType<LessThanOrEqual<PositiveInfinity, -999>>(false); 16 | expectType<LessThanOrEqual<PositiveInfinity, 999>>(false); 17 | expectType<LessThanOrEqual<999, PositiveInfinity>>(true); 18 | expectType<LessThanOrEqual<999, NegativeInfinity>>(false); 19 | expectType<LessThanOrEqual<-999, NegativeInfinity>>(false); 20 | expectType<LessThanOrEqual<PositiveInfinity, PositiveInfinity>>(true); 21 | expectType<LessThanOrEqual<NegativeInfinity, NegativeInfinity>>(true); 22 | expectType<LessThanOrEqual<PositiveInfinity, NegativeInfinity>>(false); 23 | -------------------------------------------------------------------------------- /test-d/less-than.ts: -------------------------------------------------------------------------------- 1 | import {expectType} from 'tsd'; 2 | import type {LessThan} from '../index.d.ts'; 3 | import type {NegativeInfinity, PositiveInfinity} from '../source/numeric.d.ts'; 4 | 5 | declare const never: never; 6 | 7 | expectType<LessThan<1, 2>>(true); 8 | expectType<LessThan<2, 1>>(false); 9 | expectType<LessThan<10, 2>>(false); 10 | expectType<LessThan<10, -2>>(false); 11 | expectType<LessThan<2, 2>>(false); 12 | expectType<LessThan<-2, -2>>(false); 13 | expectType<LessThan<-2, -3>>(false); 14 | expectType<LessThan<-2, number>>(never); 15 | 16 | // === unions === 17 | expectType<LessThan<10, 50 | 100>>(true); 18 | expectType<LessThan<50 | 25 | 0 | -16, 100>>(true); 19 | expectType<LessThan<1 | 2, 3 | 4>>(true); 20 | 21 | expectType<LessThan<100 | 200, 50>>(false); 22 | expectType<LessThan<25, -100 | -15 | 2 | 21>>(false); 23 | expectType<LessThan<10 | 15, -5 | 10>>(false); 24 | 25 | expectType<LessThan<-10, -90 | 90>>({} as boolean); 26 | expectType<LessThan<-16 | 16, 0>>({} as boolean); 27 | expectType<LessThan<-4 | 45, 20 | 30>>({} as boolean); 28 | expectType<LessThan<1 | -1 | 3, 0 | 2>>({} as boolean); 29 | 30 | expectType<LessThan<PositiveInfinity, -999>>(false); 31 | expectType<LessThan<PositiveInfinity, 999>>(false); 32 | expectType<LessThan<999, PositiveInfinity>>(true); 33 | expectType<LessThan<999, NegativeInfinity>>(false); 34 | expectType<LessThan<-999, NegativeInfinity>>(false); 35 | expectType<LessThan<PositiveInfinity, PositiveInfinity>>(false); 36 | expectType<LessThan<NegativeInfinity, NegativeInfinity>>(false); 37 | expectType<LessThan<PositiveInfinity, NegativeInfinity>>(false); 38 | -------------------------------------------------------------------------------- /test-d/literal-to-primitive-deep.ts: -------------------------------------------------------------------------------- 1 | import {expectType} from 'tsd'; 2 | import type {IsEqual, LiteralToPrimitiveDeep} from '../index.d.ts'; 3 | 4 | type LiteralObject = { 5 | a: string; 6 | b: number; 7 | c: boolean; 8 | d: { 9 | e: bigint; 10 | f: symbol; 11 | g: { 12 | h: string[]; 13 | i: { 14 | j: boolean; 15 | k: { 16 | l: 1; 17 | m: 'hello'; 18 | o: [1, 2, 3]; 19 | p: ['a', 'b', 'c']; 20 | q: [1, 'a', true]; 21 | }; 22 | }; 23 | }; 24 | }; 25 | }; 26 | 27 | type PrimitiveObject = { 28 | a: string; 29 | b: number; 30 | c: boolean; 31 | d: { 32 | e: bigint; 33 | f: symbol; 34 | g: { 35 | h: string[]; 36 | i: { 37 | j: boolean; 38 | k: { 39 | l: number; 40 | m: string; 41 | o: number[]; 42 | p: string[]; 43 | q: Array<number | string | boolean>; 44 | }; 45 | }; 46 | }; 47 | }; 48 | }; 49 | 50 | const typeEqual: IsEqual< 51 | LiteralToPrimitiveDeep<LiteralObject>, 52 | PrimitiveObject 53 | > = true; 54 | expectType<true>(typeEqual); 55 | -------------------------------------------------------------------------------- /test-d/literal-to-primitive.ts: -------------------------------------------------------------------------------- 1 | import {expectType} from 'tsd'; 2 | import type {LiteralToPrimitive} from '../index.d.ts'; 3 | 4 | // Simple usage 5 | declare const numberPrimitive: LiteralToPrimitive<123>; 6 | expectType<number>(numberPrimitive); 7 | 8 | const symbol = Symbol('foo'); 9 | 10 | // Union 11 | declare const kitchenSink: LiteralToPrimitive<123 | 123n | 'hello' | true | undefined | typeof symbol | null | {key: string}>; 12 | expectType<number | bigint | string | boolean | undefined | symbol | null>(kitchenSink); 13 | -------------------------------------------------------------------------------- /test-d/merge-exclusive.ts: -------------------------------------------------------------------------------- 1 | import {expectNotAssignable, expectAssignable} from 'tsd'; 2 | import type {MergeExclusive} from '../index.d.ts'; 3 | 4 | type BaseOptions = { 5 | option?: string; 6 | }; 7 | 8 | type ExclusiveVariation1 = { 9 | exclusive1: boolean; 10 | } & BaseOptions; 11 | 12 | type ExclusiveVariation2 = { 13 | exclusive2: number; 14 | } & BaseOptions; 15 | 16 | type Options = MergeExclusive<ExclusiveVariation1, ExclusiveVariation2>; 17 | 18 | const exclusiveVariation1: Options = {exclusive1: true}; 19 | const exclusiveVariation2: Options = {exclusive2: 1}; 20 | 21 | expectAssignable<{option?: string; exclusive1: boolean; exclusive2?: string}>( 22 | exclusiveVariation1, 23 | ); 24 | expectAssignable<{option?: string; exclusive1?: string; exclusive2: number}>( 25 | exclusiveVariation2, 26 | ); 27 | 28 | expectNotAssignable<Options>({exclusive1: true, exclusive2: 1}); 29 | -------------------------------------------------------------------------------- /test-d/multidimensional-array.ts: -------------------------------------------------------------------------------- 1 | import {expectType} from 'tsd'; 2 | import type {MultidimensionalArray} from '../index.d.ts'; 3 | 4 | function createArray<T extends number>(dimensions: T): MultidimensionalArray<unknown, T> { 5 | const root: unknown[] = []; 6 | 7 | let array = root; 8 | for (let dimension = 1; dimension < dimensions; ++dimension) { 9 | array[0] = []; 10 | array = array[0] as unknown[]; 11 | } 12 | 13 | return root as MultidimensionalArray<unknown, T>; 14 | } 15 | 16 | const a: MultidimensionalArray<number, 3> = []; 17 | const b: MultidimensionalArray<boolean, number> = []; 18 | const c = createArray(2); 19 | const d = createArray(7); 20 | 21 | // @ts-expect-error 22 | a[0][0][0] = 42; 23 | 24 | type RecursiveArray<T> = Array<RecursiveArray<T>>; 25 | 26 | expectType<number[][][]>(a); 27 | expectType<RecursiveArray<boolean>>(b); 28 | expectType<unknown[][]>(c); 29 | expectType<unknown[][][][][][][]>(d); 30 | -------------------------------------------------------------------------------- /test-d/multidimensional-readonly-array.ts: -------------------------------------------------------------------------------- 1 | import {expectType} from 'tsd'; 2 | import type {MultidimensionalReadonlyArray} from '../index.d.ts'; 3 | 4 | function createArray<T extends number>(dimensions: T): MultidimensionalReadonlyArray<string, T> { 5 | const root: unknown[] = []; 6 | 7 | let array = root; 8 | for (let dimension = 1; dimension < dimensions; ++dimension) { 9 | array[0] = []; 10 | if (dimension < dimensions - 1) { 11 | array = array[0] as unknown[]; 12 | } else { 13 | array[0] = '42'; 14 | } 15 | } 16 | 17 | return root as MultidimensionalReadonlyArray<unknown, T> as MultidimensionalReadonlyArray<string, T>; 18 | } 19 | 20 | const a: MultidimensionalReadonlyArray<number, 3> = []; 21 | const b: MultidimensionalReadonlyArray<boolean, number> = []; 22 | const c = createArray(2); 23 | 24 | const answer = c?.[0]?.[0]; // '42' 25 | 26 | type RecursiveArray<T> = ReadonlyArray<RecursiveArray<T>>; 27 | 28 | expectType<string | undefined>(answer); 29 | 30 | expectType<ReadonlyArray<ReadonlyArray<readonly number[]>>>(a); 31 | expectType<RecursiveArray<boolean>>(b); 32 | expectType<ReadonlyArray<readonly string[]>>(c); 33 | -------------------------------------------------------------------------------- /test-d/non-empty-object.ts: -------------------------------------------------------------------------------- 1 | import {expectNever, expectType} from 'tsd'; 2 | import type {NonEmptyObject, RequireAtLeastOne} from '../index.d.ts'; 3 | 4 | type TestType1 = { 5 | a: string; 6 | b: boolean; 7 | }; 8 | 9 | type TestType2 = { 10 | a?: string; 11 | b?: boolean; 12 | }; 13 | 14 | type TestType3 = { 15 | a: string; 16 | b?: boolean; 17 | }; 18 | 19 | type TestType4 = {}; 20 | 21 | declare const test1: NonEmptyObject<TestType1>; 22 | declare const test2: NonEmptyObject<TestType2>; 23 | declare const test3: NonEmptyObject<TestType3>; 24 | declare const test4: NonEmptyObject<TestType4>; 25 | 26 | expectType<TestType1>(test1); 27 | expectType<RequireAtLeastOne<TestType2>>(test2); 28 | expectType<TestType3>(test3); 29 | expectNever(test4); 30 | -------------------------------------------------------------------------------- /test-d/non-empty-string.ts: -------------------------------------------------------------------------------- 1 | import {expectType} from 'tsd'; 2 | import type {NonEmptyString} from '../index.d.ts'; 3 | 4 | expectType<never>({} as NonEmptyString<''>); 5 | 6 | expectType<'a'>({} as NonEmptyString<'a'>); 7 | 8 | expectType<never>({} as NonEmptyString<string>); 9 | expectType<never>({} as NonEmptyString<Uppercase<string>>); 10 | expectType<`on${string}`>({} as NonEmptyString<`on${string}`>); 11 | 12 | expectType<'a' | 'b'>({} as NonEmptyString<'a' | 'b'>); 13 | expectType<'a' | `${number}.${number}`>({} as NonEmptyString<'a' | `${number}.${number}`>); 14 | expectType<never>({} as NonEmptyString<'' | 'a'>); 15 | expectType<never>({} as NonEmptyString<'a' | Uppercase<string>>); 16 | expectType<never>({} as NonEmptyString<'' | `on${string}`>); 17 | 18 | // `NonEmptyString<S>` should be assignable to `string` 19 | type Assignability1<_S extends string> = unknown; 20 | type Test1<S extends string> = Assignability1<NonEmptyString<S>>; 21 | 22 | // `string` should NOT be assignable to `NonEmptyString<S>` 23 | type Assignability2<_S extends string, _SS extends NonEmptyString<_S>> = unknown; 24 | // @ts-expect-error 25 | type Test2<S extends string> = Assignability2<S, S>; 26 | -------------------------------------------------------------------------------- /test-d/non-empty-tuple.ts: -------------------------------------------------------------------------------- 1 | import {expectType} from 'tsd'; 2 | import type {NonEmptyTuple} from '../index.d.ts'; 3 | 4 | declare const sum: (...numbers: NonEmptyTuple<number>) => number; 5 | 6 | expectType<number>(sum(1, 2, 3)); 7 | expectType<number>(sum(1)); 8 | 9 | // @ts-expect-error 10 | sum(); 11 | -------------------------------------------------------------------------------- /test-d/observable-like.ts: -------------------------------------------------------------------------------- 1 | import {expectType, expectAssignable} from 'tsd'; 2 | import type {ObservableLike} from '../source/globals/index.d.ts'; 3 | 4 | // eslint-disable-next-line no-use-extend-native/no-use-extend-native 5 | expectAssignable<symbol>(Symbol.observable); 6 | 7 | const observable = (null as any) as ObservableLike; 8 | 9 | const subscription = observable.subscribe({ 10 | next() {}, // eslint-disable-line @typescript-eslint/no-empty-function 11 | }); 12 | expectType<{unsubscribe(): void}>(subscription); 13 | 14 | observable.subscribe({ 15 | next(value) { 16 | expectType<unknown>(value); 17 | }, 18 | }); 19 | 20 | const observable2 = (null as any) as ObservableLike<string>; 21 | 22 | observable2.subscribe({ 23 | next() {}, // eslint-disable-line @typescript-eslint/no-empty-function 24 | }); 25 | observable2.subscribe({ 26 | next(value) { 27 | expectType<string>(value); 28 | }, 29 | }); 30 | -------------------------------------------------------------------------------- /test-d/omit-index-signature.ts: -------------------------------------------------------------------------------- 1 | import {expectType} from 'tsd'; 2 | import type {OmitIndexSignature} from '../index.d.ts'; 3 | 4 | type ExampleInterface = { 5 | // These index signatures will be removed. 6 | [x: string]: any; 7 | [x: number]: any; 8 | [x: symbol]: any; 9 | [x: `head-${string}`]: string; 10 | [x: `${string}-tail`]: string; 11 | [x: `head-${string}-tail`]: string; 12 | [x: `${bigint}`]: string; 13 | [x: `embedded-${number}`]: string; 14 | 15 | // These explicitly defined keys will remain. 16 | foo: 'bar'; 17 | qux?: 'baz'; 18 | }; 19 | 20 | type MappedType<ObjectType> = { 21 | [Key in keyof ObjectType]: { 22 | key: Key; 23 | value: Exclude<ObjectType[Key], undefined>; 24 | }; 25 | }; 26 | 27 | declare const exampleInterfaceKnownKeys: OmitIndexSignature<ExampleInterface>; 28 | expectType<{ 29 | foo: 'bar'; 30 | qux?: 'baz'; 31 | }>(exampleInterfaceKnownKeys); 32 | 33 | declare const exampleMappedTypeKnownKeys: OmitIndexSignature< 34 | MappedType<ExampleInterface> 35 | >; 36 | expectType<{ 37 | foo: {key: 'foo'; value: 'bar'}; 38 | qux?: {key: 'qux'; value: 'baz'}; 39 | }>(exampleMappedTypeKnownKeys); 40 | -------------------------------------------------------------------------------- /test-d/or.ts: -------------------------------------------------------------------------------- 1 | import {expectType} from 'tsd'; 2 | import type {Or} from '../source/or.d.ts'; 3 | 4 | expectType<Or<true, true>>(true); 5 | expectType<Or<true, false>>(true); 6 | expectType<Or<false, true>>(true); 7 | expectType<Or<false, false>>(false); 8 | 9 | expectType<Or<true, boolean>>(true); 10 | expectType<Or<boolean, true>>(true); 11 | expectType<Or<false, boolean>>({} as boolean); 12 | expectType<Or<boolean, false>>({} as boolean); 13 | expectType<Or<boolean, boolean>>({} as boolean); 14 | 15 | expectType<Or<true, any>>(true); 16 | expectType<Or<any, true>>(true); 17 | expectType<Or<false, any>>({} as boolean); 18 | expectType<Or<any, false>>({} as boolean); 19 | expectType<Or<boolean, any>>({} as boolean); 20 | expectType<Or<any, boolean>>({} as boolean); 21 | expectType<Or<any, any>>({} as boolean); 22 | 23 | expectType<Or<true, never>>(true); 24 | expectType<Or<never, true>>(true); 25 | expectType<Or<false, never>>(false); 26 | expectType<Or<never, false>>(false); 27 | expectType<Or<boolean, never>>({} as boolean); 28 | expectType<Or<never, boolean>>({} as boolean); 29 | expectType<Or<never, never>>(false); 30 | -------------------------------------------------------------------------------- /test-d/override-properties.ts: -------------------------------------------------------------------------------- 1 | import {expectType} from 'tsd'; 2 | import {expectTypeOf} from 'expect-type'; 3 | import type {OverrideProperties} from '../source/override-properties.d.ts'; 4 | 5 | type Foo = { 6 | a: number; 7 | b: string; 8 | }; 9 | 10 | const fixture: OverrideProperties<Foo, {b: number}> = {a: 1, b: 2}; 11 | expectType<{a: number; b: number}>(fixture); 12 | 13 | // @ts-expect-error 14 | type Bar = OverrideProperties<Foo, {c: number}>; 15 | 16 | // @ts-expect-error 17 | type Bar = OverrideProperties<Foo, {b: number; c: number}>; 18 | 19 | // Test for https://github.com/sindresorhus/type-fest/issues/858 20 | { // eslint-disable-line no-lone-blocks 21 | type Original = { 22 | foo: string; 23 | bar: string; 24 | }; 25 | 26 | type Modified = { 27 | foo: string | undefined; 28 | bar: string; 29 | }; 30 | 31 | type Final = OverrideProperties<Original, Modified>; 32 | 33 | expectTypeOf<Final>().toMatchTypeOf<{foo: string | undefined; bar: string}>(); 34 | } 35 | -------------------------------------------------------------------------------- /test-d/pascal-case.ts: -------------------------------------------------------------------------------- 1 | import {expectType} from 'tsd'; 2 | import type {PascalCase} from '../index.d.ts'; 3 | 4 | const pascalFromCamel: PascalCase<'fooBar'> = 'FooBar'; 5 | expectType<'FooBar'>(pascalFromCamel); 6 | 7 | const pascalFromKebab: PascalCase<'foo-bar'> = 'FooBar'; 8 | expectType<'FooBar'>(pascalFromKebab); 9 | 10 | const pascalFromComplexKebab: PascalCase<'foo-bar-abc-123'> = 'FooBarAbc123'; 11 | expectType<'FooBarAbc123'>(pascalFromComplexKebab); 12 | 13 | expectType<PascalCase<'fooBAR'>>('FooBar'); 14 | expectType<PascalCase<'fooBAR', {preserveConsecutiveUppercase: true}>>('FooBAR'); 15 | 16 | expectType<PascalCase<'fooBARBiz'>>('FooBarBiz'); 17 | expectType<PascalCase<'fooBARBiz', {preserveConsecutiveUppercase: true}>>('FooBARBiz'); 18 | 19 | expectType<PascalCase<'foo BAR-Biz_BUZZ', {preserveConsecutiveUppercase: true}>>('FooBARBizBUZZ'); 20 | expectType<PascalCase<'foo BAR-Biz_BUZZ', {preserveConsecutiveUppercase: false}>>('FooBarBizBuzz'); 21 | expectType<PascalCase<'foo\tBAR-Biz_BUZZ'>>('FooBarBizBuzz'); 22 | 23 | expectType<PascalCase<string, {preserveConsecutiveUppercase: true}>>({} as Capitalize<string>); 24 | expectType<PascalCase<string>>({} as Capitalize<string>); 25 | -------------------------------------------------------------------------------- /test-d/pascal-cased-properties.ts: -------------------------------------------------------------------------------- 1 | import {expectType} from 'tsd'; 2 | import type {PascalCasedProperties} from '../index.d.ts'; 3 | 4 | declare const foo: PascalCasedProperties<{helloWorld: {fooBar: string}}>; 5 | expectType<{HelloWorld: {fooBar: string}}>(foo); 6 | 7 | declare const bar: PascalCasedProperties<Array<{helloWorld: string}>>; 8 | expectType<Array<{helloWorld: string}>>(bar); 9 | 10 | declare const fooBar: PascalCasedProperties<() => {a: string}>; 11 | expectType<() => {a: string}>(fooBar); 12 | 13 | // Verify example 14 | type User = { 15 | userId: number; 16 | userName: string; 17 | }; 18 | const result: PascalCasedProperties<User> = { 19 | UserId: 1, 20 | UserName: 'Tom', 21 | }; 22 | expectType<PascalCasedProperties<User>>(result); 23 | 24 | declare const baz: PascalCasedProperties<{fooBAR: number; BARFoo: string}, {preserveConsecutiveUppercase: true}>; 25 | expectType<{FooBAR: number; BARFoo: string}>(baz); 26 | 27 | declare const biz: PascalCasedProperties<{fooBAR: number; BARFoo: string}>; 28 | expectType<{FooBar: number; BarFoo: string}>(biz); 29 | -------------------------------------------------------------------------------- /test-d/pick-index-signature.ts: -------------------------------------------------------------------------------- 1 | import {expectType} from 'tsd'; 2 | import type {PickIndexSignature, Simplify} from '../index.d.ts'; 3 | 4 | declare const symbolKey: unique symbol; 5 | 6 | type Foo = { 7 | [x: string]: unknown; 8 | [x: number]: unknown; 9 | [x: symbol]: unknown; 10 | [x: `head-${string}`]: string; 11 | [x: `${string}-tail`]: string; 12 | [x: `head-${string}-tail`]: string; 13 | [x: `${bigint}`]: string; 14 | [x: `embedded-${number}`]: string; 15 | }; 16 | 17 | type Bar = { 18 | ['kebab-case-key']: string; 19 | [symbolKey]: string; 20 | foo: 'bar'; 21 | qux?: 'baz'; 22 | }; 23 | 24 | type FooBar = Simplify<Foo & Bar>; 25 | 26 | declare const indexSignature: PickIndexSignature<FooBar>; 27 | 28 | expectType<Foo>(indexSignature); 29 | -------------------------------------------------------------------------------- /test-d/promisable.ts: -------------------------------------------------------------------------------- 1 | import {expectType} from 'tsd'; 2 | import type {Promisable} from '../index.d.ts'; 3 | 4 | declare const promisable: Promisable<string>; 5 | expectType<PromiseLike<string> | string>(promisable); 6 | -------------------------------------------------------------------------------- /test-d/readonly-tuple.ts: -------------------------------------------------------------------------------- 1 | import {expectAssignable, expectNotAssignable} from 'tsd'; 2 | import type {ReadonlyTuple} from '../index.d.ts'; 3 | 4 | type TupleOfThreeStrings = ReadonlyTuple<string, 3>; 5 | 6 | expectAssignable<TupleOfThreeStrings>(['a', 'b', 'c']); 7 | 8 | expectNotAssignable<TupleOfThreeStrings>(['a', 'b', 123]); 9 | expectNotAssignable<TupleOfThreeStrings>(['a']); 10 | expectNotAssignable<TupleOfThreeStrings>(['a', 'b']); 11 | expectNotAssignable<TupleOfThreeStrings>(['a', 'b', 'c', 'd']); 12 | 13 | declare const test: TupleOfThreeStrings; 14 | 15 | // @ts-expect-error 16 | const _a: unknown = test.push; 17 | // @ts-expect-error 18 | test[2] = 'a'; 19 | -------------------------------------------------------------------------------- /test-d/set-non-nullable.ts: -------------------------------------------------------------------------------- 1 | import {expectNotAssignable, expectType} from 'tsd'; 2 | import type {SetNonNullable} from '../index.d.ts'; 3 | 4 | // Update one possibly undefined key and one possibly null key to non-nullable. 5 | declare const variation1: SetNonNullable<{a: number; b: string | undefined; c: boolean | null}, 'b' | 'c'>; 6 | expectType<{a: number; b: string; c: boolean}>(variation1); 7 | 8 | // Update a key that is possibly null or undefined. 9 | declare const variation2: SetNonNullable<{a: number; b: string | null | undefined}, 'b'>; 10 | expectType<{a: number; b: string}>(variation2); 11 | 12 | // Update an optional key. 13 | declare const variation3: SetNonNullable<{a: number; b?: string | undefined}, 'b'>; 14 | expectType<{a: number; b?: string}>(variation3); 15 | 16 | // Fail if type changes even if non-nullable is right. 17 | declare const variation4: SetNonNullable<{a: number; b: string | undefined}, 'b'>; 18 | expectNotAssignable<{a: string; b: string}>(variation4); 19 | 20 | // Update all keys if `Keys` generic is not passed. 21 | declare const variation5: SetNonNullable<{a: number; b: string | undefined; c: boolean | null}>; 22 | expectType<{a: number; b: string; c: boolean}>(variation5); 23 | 24 | // Does not throw type error in type predicate contexts. 25 | type Variation6Config = {a: boolean | null; b: boolean | null}; 26 | const variant6Function = <TProperty extends keyof Variation6Config>( 27 | config: Variation6Config, 28 | property: TProperty, 29 | ): config is SetNonNullable<Variation6Config, TProperty> => Boolean(config[property]); 30 | expectNotAssignable<never>(variant6Function); // Just to prevent unused error. 31 | -------------------------------------------------------------------------------- /test-d/set-return-type.ts: -------------------------------------------------------------------------------- 1 | import {expectType} from 'tsd'; 2 | import type {SetReturnType} from '../index.d.ts'; 3 | 4 | declare const anything: unknown; 5 | 6 | // Without `thisArg` and without parameters. 7 | declare const variation1: SetReturnType<() => void, number>; 8 | expectType<() => number>(variation1); 9 | variation1.call(anything); 10 | 11 | // Without `thisArg` and with parameters. 12 | declare const variation2: SetReturnType<(foo: string, bar: boolean) => number, void>; 13 | expectType<(foo: string, bar: boolean) => void>(variation2); 14 | variation2.call(anything, 'foo', true); 15 | 16 | // With `thisArg` and without parameters. 17 | function function1(this: Date): void {} // eslint-disable-line @typescript-eslint/no-empty-function 18 | declare const variation3: SetReturnType<typeof function1, string[]>; 19 | expectType<(this: Date) => string[]>(variation3); 20 | variation3.call(new Date()); 21 | // @ts-expect-error 22 | variation3.call('not-a-date'); 23 | 24 | // With `thisArg` and with parameters. 25 | declare function function2(this: Date, foo: any, bar: Array<[number]>): any; 26 | declare const variation4: SetReturnType<typeof function2, never>; 27 | expectType<(this: Date, foo: any, bar: Array<[number]>) => never>(variation4); 28 | variation4.call(new Date(), anything, [[4], [7]]); 29 | // @ts-expect-error 30 | variation4.call('not-a-date', anything, [[4], [7]]); 31 | 32 | // Sanity check to the fact that omitting `this: unknown` from the argument list has no effect other than in readability. 33 | declare function withExplicitThis(this: unknown, foo: string): number; 34 | declare function withImplicitThis(foo: string): number; 35 | expectType<typeof withExplicitThis>(withImplicitThis); 36 | expectType<typeof withImplicitThis>(withExplicitThis); 37 | -------------------------------------------------------------------------------- /test-d/simplify-deep.ts: -------------------------------------------------------------------------------- 1 | import {expectType} from 'tsd'; 2 | import type {SimplifyDeep} from '../index.d.ts'; 3 | 4 | type Properties1 = { 5 | height: number; 6 | position: { 7 | top: number; 8 | bottom: number; 9 | }; 10 | }; 11 | 12 | type Properties2 = { 13 | width: number; 14 | position: { 15 | left: number; 16 | right: number; 17 | }; 18 | }; 19 | 20 | // Flatten the type output to improve type hints shown in editors. 21 | declare const flattenProperties: { 22 | height: number; 23 | width: number; 24 | position: { 25 | top: number; 26 | bottom: number; 27 | left: number; 28 | right: number; 29 | }; 30 | }; 31 | 32 | expectType<SimplifyDeep<Properties1 & Properties2>>(flattenProperties); 33 | 34 | // Array 35 | type ArrayType = Array<{ 36 | a: string; 37 | }>; 38 | 39 | declare const flattenProperties2: { 40 | arrayType: Array<{ 41 | a: string; 42 | }>; 43 | }; 44 | expectType<SimplifyDeep<{arrayType: ArrayType}>>(flattenProperties2); 45 | -------------------------------------------------------------------------------- /test-d/single-key-object.ts: -------------------------------------------------------------------------------- 1 | import {expectNever, expectAssignable} from 'tsd'; 2 | import type {SingleKeyObject} from '../index.d.ts'; 3 | 4 | const test = <T>(_: SingleKeyObject<T>): void => {}; // eslint-disable-line @typescript-eslint/no-empty-function 5 | 6 | test({key: 'value'}); 7 | 8 | // @ts-expect-error 9 | test({}); 10 | // @ts-expect-error 11 | test({key: 'value', otherKey: 'other value'}); 12 | 13 | declare const validObject: SingleKeyObject<{key: string}>; 14 | expectAssignable<{key: string}>(validObject); 15 | 16 | declare const invalidObject: SingleKeyObject<{key1: string; key2: number}>; 17 | expectNever(invalidObject); 18 | -------------------------------------------------------------------------------- /test-d/snake-cased-properties-deep.ts: -------------------------------------------------------------------------------- 1 | import {expectType} from 'tsd'; 2 | import type {SnakeCasedPropertiesDeep} from '../index.d.ts'; 3 | 4 | type FooBar = {helloWorld: {p2p: Array<{addressLine1: string}>}}; 5 | 6 | declare const foo: SnakeCasedPropertiesDeep<FooBar>; 7 | expectType<{hello_world: {p2p: Array<{address_line1: string}>}}>(foo); 8 | 9 | declare const bar: SnakeCasedPropertiesDeep<FooBar, {splitOnNumbers: true}>; 10 | expectType<{hello_world: {p_2_p: Array<{address_line_1: string}>}}>(bar); 11 | 12 | // Verify example 13 | type User = { 14 | userId: number; 15 | userName: string; 16 | date: Date; 17 | regExp: RegExp; 18 | }; 19 | 20 | type UserWithFriends = { 21 | userInfo: User; 22 | userFriends: User[]; 23 | }; 24 | 25 | const result: SnakeCasedPropertiesDeep<UserWithFriends> = { 26 | user_info: { 27 | user_id: 1, 28 | user_name: 'Tom', 29 | date: new Date(), 30 | reg_exp: /.*/, 31 | }, 32 | user_friends: [ 33 | { 34 | user_id: 2, 35 | user_name: 'Jerry', 36 | date: new Date(), 37 | reg_exp: /.*/, 38 | }, 39 | { 40 | user_id: 3, 41 | user_name: 'Spike', 42 | date: new Date(), 43 | reg_exp: /.*/, 44 | }, 45 | ], 46 | }; 47 | expectType<SnakeCasedPropertiesDeep<UserWithFriends>>(result); 48 | 49 | expectType<{foo_bar: unknown}>({} as SnakeCasedPropertiesDeep<{fooBar: unknown}>); 50 | expectType<{foo_bar: {bar_baz: unknown}; biz: unknown}>({} as SnakeCasedPropertiesDeep<{fooBar: {barBaz: unknown}; biz: unknown}>); 51 | 52 | expectType<{foo_bar: any}>({} as SnakeCasedPropertiesDeep<{fooBar: any}>); 53 | expectType<{foo_bar: {bar_baz: any}; biz: any}>({} as SnakeCasedPropertiesDeep<{fooBar: {barBaz: any}; biz: any}>); 54 | -------------------------------------------------------------------------------- /test-d/snake-cased-properties.ts: -------------------------------------------------------------------------------- 1 | import {expectType} from 'tsd'; 2 | import type {SnakeCasedProperties} from '../index.d.ts'; 3 | 4 | type Foobar = {helloWorld1: {fooBar: string}}; 5 | 6 | declare const foo: SnakeCasedProperties<Foobar>; 7 | expectType<{hello_world1: {fooBar: string}}>(foo); 8 | 9 | declare const bar: SnakeCasedProperties<Foobar, {splitOnNumbers: true}>; 10 | expectType<{hello_world_1: {fooBar: string}}>(bar); 11 | 12 | // Verify example 13 | type User = { 14 | userId: number; 15 | userName: string; 16 | }; 17 | const result: SnakeCasedProperties<User> = { 18 | user_id: 1, 19 | user_name: 'Tom', 20 | }; 21 | expectType<SnakeCasedProperties<User>>(result); 22 | -------------------------------------------------------------------------------- /test-d/string-slice.ts: -------------------------------------------------------------------------------- 1 | import {expectType} from 'tsd'; 2 | import type {StringSlice} from '../index.d.ts'; 3 | 4 | expectType<StringSlice<'abcde'>>('abcde'); 5 | expectType<StringSlice<'abcde'>>('abcde'); 6 | expectType<StringSlice<'abcde', 0, -1>>('abcd'); 7 | expectType<StringSlice<'abcde', 1, -1>>('bcd'); 8 | expectType<StringSlice<'abcde', 1, 2>>('b'); 9 | expectType<StringSlice<'abcde', 1, 3>>('bc'); 10 | expectType<StringSlice<'abcde', -100, -1>>('abcd'); 11 | expectType<StringSlice<'abcde', -100, -3>>('ab'); 12 | expectType<StringSlice<'abcde', 3, 100>>('de'); 13 | expectType<StringSlice<'abcde', 1, 1>>(''); 14 | expectType<StringSlice<'abcde', 100, 1>>(''); 15 | expectType<StringSlice<string>>(null! as string); 16 | expectType<StringSlice<string, 1>>(null! as string); 17 | expectType<StringSlice<string, 1, 2>>(null! as string); 18 | -------------------------------------------------------------------------------- /test-d/stringified.ts: -------------------------------------------------------------------------------- 1 | import {expectAssignable, expectNotAssignable, expectType} from 'tsd'; 2 | import type {Stringified} from '../index.d.ts'; 3 | 4 | declare const stringified: Stringified<{a: number; b: string}>; 5 | expectType<{a: string; b: string}>(stringified); 6 | 7 | type Car = { 8 | model: string; 9 | speed: number; 10 | }; 11 | expectNotAssignable<Stringified<Car>>({model: 'Foo', speed: 101}); 12 | expectAssignable<Stringified<Car>>({model: 'Foo', speed: '101'}); 13 | -------------------------------------------------------------------------------- /test-d/tagged-union.ts: -------------------------------------------------------------------------------- 1 | import {expectAssignable, expectNotAssignable} from 'tsd'; 2 | import type {TaggedUnion} from '../index.d.ts'; 3 | 4 | type Union = TaggedUnion<'tag', {str: {a: string}; num: {b: number}}>; 5 | 6 | const first = { 7 | tag: 'str' as const, 8 | a: 'some-string', 9 | }; 10 | 11 | const second = { 12 | tag: 'num' as const, 13 | b: 1, 14 | }; 15 | 16 | expectAssignable<Union>(first); 17 | expectAssignable<Union>(second); 18 | 19 | const fails = { 20 | tag: 'num' as const, 21 | b: 'should not be string', 22 | }; 23 | 24 | const failsToo = { 25 | tag: 'str' as const, 26 | b: 2, 27 | }; 28 | 29 | expectNotAssignable<Union>(fails); 30 | expectNotAssignable<Union>(failsToo); 31 | -------------------------------------------------------------------------------- /test-d/trim.ts: -------------------------------------------------------------------------------- 1 | import {expectType} from 'tsd'; 2 | import type {Trim} from '../index.d.ts'; 3 | 4 | declare function trim<S extends string>(value: S): Trim<S>; 5 | 6 | expectType<'foo'>(trim(' foo')); 7 | expectType<'bar'>(trim('bar ')); 8 | expectType<'baz'>(trim(' baz ')); 9 | expectType<'waldo'>(trim(' waldo ')); 10 | expectType<'fr ed'>(trim(' fr ed ')); 11 | expectType<'foo'>(trim(' foo\n')); 12 | expectType<'foo'>(trim(' foo\n\t ')); 13 | -------------------------------------------------------------------------------- /test-d/ts41.ts: -------------------------------------------------------------------------------- 1 | // Ensure that TypeScript 4.1 types are available. 2 | import {expectType} from 'tsd'; 3 | import type {CamelCase} from '../index.d.ts'; 4 | 5 | const camelFromPascal: CamelCase<'FooBar'> = 'fooBar'; 6 | expectType<CamelCase<'FooBar'>>(camelFromPascal); 7 | -------------------------------------------------------------------------------- /test-d/tsconfig-json.ts: -------------------------------------------------------------------------------- 1 | import {expectType, expectAssignable} from 'tsd'; 2 | import type {Jsonifiable, TsConfigJson} from '../index.d.ts'; 3 | 4 | const tsConfig: TsConfigJson = {}; 5 | 6 | expectType<boolean | undefined>(tsConfig.compileOnSave); 7 | expectType<TsConfigJson.CompilerOptions | undefined>(tsConfig.compilerOptions); 8 | expectType<string[] | undefined>(tsConfig.exclude); 9 | expectType<string | string[] | undefined>(tsConfig.extends); 10 | expectType<string[] | undefined>(tsConfig.files); 11 | expectType<string[] | undefined>(tsConfig.include); 12 | expectType<TsConfigJson.References[] | undefined>(tsConfig.references); 13 | expectType<TsConfigJson.TypeAcquisition | undefined>(tsConfig.typeAcquisition); 14 | expectAssignable<Jsonifiable>(tsConfig); 15 | 16 | expectType<boolean | undefined>(tsConfig.compilerOptions?.noCheck); 17 | -------------------------------------------------------------------------------- /test-d/tuple-to-union.ts: -------------------------------------------------------------------------------- 1 | import {expectAssignable, expectNotType, expectType} from 'tsd'; 2 | import type {TupleToUnion} from '../index.d.ts'; 3 | 4 | const options = ['a', 'b', 'c'] as const; 5 | type Options = TupleToUnion<typeof options>; 6 | 7 | const a: Options = 'a'; 8 | expectAssignable<Options>(a); 9 | expectType<'a'>(a); 10 | expectNotType<'b'>(a); 11 | expectNotType<'c'>(a); 12 | 13 | const b: Options = 'b'; 14 | expectAssignable<Options>(b); 15 | expectNotType<'a'>(b); 16 | expectType<'b'>(b); 17 | expectNotType<'c'>(b); 18 | 19 | const c: Options = 'c'; 20 | expectAssignable<Options>(c); 21 | expectNotType<'a'>(c); 22 | expectNotType<'b'>(c); 23 | expectType<'c'>(c); 24 | 25 | declare const notAnArray: TupleToUnion<[]>; 26 | expectType<never>(notAnArray); 27 | 28 | declare const worksWithArrays: TupleToUnion<Array<string | number>>; 29 | expectType<string | number>(worksWithArrays); 30 | 31 | declare const resolvesToNeverForNonArrays: TupleToUnion<string | number>; 32 | expectType<never>(resolvesToNeverForNonArrays); 33 | 34 | declare const infiniteRestArguments: TupleToUnion<[string, ...number[]]>; 35 | expectType<string | number>(infiniteRestArguments); 36 | -------------------------------------------------------------------------------- /test-d/union-to-intersection.ts: -------------------------------------------------------------------------------- 1 | import {expectAssignable, expectType} from 'tsd'; 2 | import type {UnionToIntersection} from '../index.d.ts'; 3 | 4 | declare const intersection1: UnionToIntersection<{a: string} | {b: number}>; 5 | expectAssignable<{a: string; b: number}>(intersection1); 6 | 7 | // Creates a union of matching properties. 8 | declare const intersection2: UnionToIntersection<{a: string} | {b: number} | {a: () => void}>; 9 | expectAssignable<{a: string | (() => void); b: number}>(intersection2); 10 | 11 | // It's possible to index by the resulting type. 12 | type ObjectsUnion = {a: string; z: string} | {b: string; z: string} | {c: string; z: string}; 13 | declare const value: ObjectsUnion[UnionToIntersection<keyof ObjectsUnion>]; 14 | expectType<string>(value); 15 | -------------------------------------------------------------------------------- /test-d/union-to-tuple.ts: -------------------------------------------------------------------------------- 1 | import {expectAssignable, expectType} from 'tsd'; 2 | import type {UnionToTuple} from '../index.d.ts'; 3 | 4 | type Options = UnionToTuple<'a' | 'b' | 'c'>; 5 | // Results unordered 6 | expectAssignable<['a', 'b', 'c'] | ['a', 'c', 'b'] | ['b', 'a', 'c'] | ['b', 'c', 'a'] | ['c', 'a', 'b'] | ['c', 'b', 'a']>({} as Options); 7 | expectType<Options[number]>({} as ('a' | 'b' | 'c')); 8 | 9 | type Options1 = UnionToTuple<1 | 2 | 3>; 10 | expectType<Options1[number]>({} as (1 | 2 | 3)); 11 | 12 | type Options2 = UnionToTuple<boolean | 1>; 13 | expectType<Options2[number]>({} as (1 | false | true)); 14 | -------------------------------------------------------------------------------- /test-d/unknown-array.ts: -------------------------------------------------------------------------------- 1 | import {expectAssignable, expectNotAssignable, expectType} from 'tsd'; 2 | import type {UnknownArray} from '../index.d.ts'; 3 | 4 | declare const foo: readonly []; 5 | declare const bar: { 6 | readonly array: unknown[]; 7 | }; 8 | 9 | expectAssignable<UnknownArray>(foo); 10 | expectAssignable<UnknownArray>(bar.array); 11 | expectAssignable<UnknownArray>([]); 12 | expectAssignable<UnknownArray>(['foo']); 13 | 14 | expectNotAssignable<UnknownArray>(null); 15 | expectNotAssignable<UnknownArray>(undefined); 16 | expectNotAssignable<UnknownArray>({}); 17 | expectNotAssignable<UnknownArray>({0: 1}); 18 | expectNotAssignable<UnknownArray>(1); 19 | expectNotAssignable<UnknownArray>(Date); 20 | 21 | type IsArray<T> = T extends UnknownArray ? true : false; 22 | 23 | declare const string: IsArray<string>; 24 | expectType<false>(string); 25 | declare const array: IsArray<[]>; 26 | expectType<true>(array); 27 | declare const tuple: IsArray<['foo']>; 28 | expectType<true>(tuple); 29 | declare const readonlyArray: IsArray<readonly number[]>; 30 | expectType<true>(readonlyArray); 31 | declare const leadingSpread: IsArray<readonly [number, ...string[]]>; 32 | expectType<true>(leadingSpread); 33 | declare const trailingSpread: IsArray<readonly [...string[], number]>; 34 | expectType<true>(trailingSpread); 35 | -------------------------------------------------------------------------------- /test-d/unknown-map.ts: -------------------------------------------------------------------------------- 1 | import {expectAssignable, expectNotAssignable} from 'tsd'; 2 | import type {UnknownMap} from '../index.d.ts'; 3 | 4 | declare const foo: ReadonlyMap<string, number>; 5 | declare const bar: { 6 | readonly map: ReadonlyMap<number, string>; 7 | }; 8 | 9 | expectAssignable<UnknownMap>(foo); 10 | expectAssignable<UnknownMap>(bar.map); 11 | expectAssignable<UnknownMap>(new Map()); 12 | expectAssignable<UnknownMap>(new Map([['foo', 1]])); 13 | 14 | expectNotAssignable<UnknownMap>(null); 15 | expectNotAssignable<UnknownMap>(undefined); 16 | expectNotAssignable<UnknownMap>({}); 17 | expectNotAssignable<UnknownMap>([]); 18 | expectNotAssignable<UnknownMap>({0: 1}); 19 | expectNotAssignable<UnknownMap>(1); 20 | -------------------------------------------------------------------------------- /test-d/unknown-record.ts: -------------------------------------------------------------------------------- 1 | import {expectAssignable, expectType} from 'tsd'; 2 | import type {UnknownRecord} from '../index.d.ts'; 3 | 4 | declare let foo: UnknownRecord; 5 | 6 | expectAssignable<UnknownRecord>(foo); 7 | expectAssignable<UnknownRecord>(foo = {}); 8 | expectAssignable<UnknownRecord>(foo = {bar: 'baz'}); 9 | expectAssignable<UnknownRecord>(foo = {bar: {baz: 'hello'}}); 10 | 11 | // @ts-expect-error 12 | foo = []; 13 | // @ts-expect-error 14 | foo = 42; 15 | // @ts-expect-error 16 | foo = null; 17 | 18 | expectType<unknown>(foo['bar']); 19 | -------------------------------------------------------------------------------- /test-d/unknown-set.ts: -------------------------------------------------------------------------------- 1 | import {expectAssignable, expectNotAssignable} from 'tsd'; 2 | import type {UnknownSet} from '../index.d.ts'; 3 | 4 | declare const foo: ReadonlySet<string>; 5 | declare const bar: { 6 | readonly set: ReadonlySet<number>; 7 | }; 8 | 9 | expectAssignable<UnknownSet>(foo); 10 | expectAssignable<UnknownSet>(bar.set); 11 | expectAssignable<UnknownSet>(new Set()); 12 | expectAssignable<UnknownSet>(new Set('foo')); 13 | 14 | expectNotAssignable<UnknownSet>(null); 15 | expectNotAssignable<UnknownSet>(undefined); 16 | expectNotAssignable<UnknownSet>({}); 17 | expectNotAssignable<UnknownSet>([]); 18 | expectNotAssignable<UnknownSet>({0: 1}); 19 | expectNotAssignable<UnknownSet>(1); 20 | expectNotAssignable<UnknownSet>(Date); 21 | -------------------------------------------------------------------------------- /test-d/value-of.ts: -------------------------------------------------------------------------------- 1 | import {expectAssignable, expectNotAssignable, expectType} from 'tsd'; 2 | import type {ValueOf} from '../index.d.ts'; 3 | 4 | const value: ValueOf<{a: 1; b: 2; c: 3}> = 3; 5 | const valueRestricted: ValueOf<{a: 1; b: 2; c: 3}, 'a'> = 1; 6 | 7 | expectAssignable<1 | 2 | 3>(value); 8 | expectNotAssignable<4>(value); 9 | 10 | expectType<1>(valueRestricted); 11 | expectNotAssignable<2>(valueRestricted); 12 | expectNotAssignable<4>(valueRestricted); 13 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@sindresorhus/tsconfig", 3 | "compilerOptions": { 4 | "noEmit": true, 5 | "allowJs": true, 6 | "noUnusedLocals": false, // Allow unused variables in test-d/*.ts files 7 | "module": "node18", 8 | "target": "ES2023", // Node.js 20 9 | "lib": [ 10 | "ES2023", 11 | "DOM" 12 | ], 13 | "types": [], // Ensures no @types/ are unintentionally included 14 | "exactOptionalPropertyTypes": true, 15 | "skipLibCheck": false, // Ensures .d.ts files are checked: https://github.com/sindresorhus/tsconfig/issues/15 16 | } 17 | } 18 | --------------------------------------------------------------------------------