Skip to content

Commit

Permalink
Add FindGlobalType and FindGlobalInstanceType types (#908)
Browse files Browse the repository at this point in the history
Co-authored-by: Sindre Sorhus <[email protected]>
  • Loading branch information
thecodewarrior and sindresorhus authored Jul 16, 2024
1 parent 3b1ceeb commit 0086cd6
Show file tree
Hide file tree
Showing 5 changed files with 170 additions and 27 deletions.
1 change: 1 addition & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ export type {IfNull} from './source/if-null';
export type {And} from './source/and';
export type {Or} from './source/or';
export type {NonEmptyTuple} from './source/non-empty-tuple';
export type {FindGlobalInstanceType, FindGlobalType} from './source/find-global-type';

// Template literal types
export type {CamelCase} from './source/camel-case';
Expand Down
2 changes: 2 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,8 @@ Click the type names for complete docs.
- [`And`](source/and.d.ts) - Returns a boolean for whether two given types are both true.
- [`Or`](source/or.d.ts) - Returns a boolean for whether either of two given types are true.
- [`NonEmptyTuple`](source/non-empty-tuple.d.ts) - Matches any non-empty tuple.
- [`FindGlobalType`](source/find-global-type.d.ts) - Tries to find the type of a global with the given name.
- [`FindGlobalInstanceType`](source/find-global-type.d.ts) - Tries to find one or more types from their globally-defined constructors.

### Type Guard

Expand Down
64 changes: 64 additions & 0 deletions source/find-global-type.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/**
Tries to find the type of a global with the given name.
Limitations: Due to peculiarities with the behavior of `globalThis`, "globally defined" only includes `var` declarations in `declare global` blocks, not `let` or `const` declarations.
@example
```
import type {FindGlobalType} from 'type-fest';
declare global {
const foo: number; // let and const don't work
var bar: string; // var works
}
type FooType = FindGlobalType<'foo'> //=> never (let/const don't work)
type BarType = FindGlobalType<'bar'> //=> string
type OtherType = FindGlobalType<'other'> //=> never (no global named 'other')
```
@category Utilities
*/
export type FindGlobalType<Name extends string> = typeof globalThis extends Record<Name, infer T> ? T : never;

/**
Tries to find one or more types from their globally-defined constructors.
Use-case: Conditionally referencing DOM types only when the DOM library present.
*Limitations:* Due to peculiarities with the behavior of `globalThis`, "globally defined" has a narrow definition in this case. Declaring a class in a `declare global` block won't work, instead you must declare its type using an interface and declare its constructor as a `var` (*not* `let`/`const`) inside the `declare global` block.
@example
```
import type {FindGlobalInstanceType} from 'type-fest';
class Point {
constructor(public x: number, public y: number) {}
}
type PointLike = Point | FindGlobalInstanceType<'DOMPoint'>;
```
@example
```
import type {FindGlobalInstanceType} from 'type-fest';
declare global {
// Class syntax won't add the key to `globalThis`
class Foo {}
// interface + constructor style works
interface Bar {}
var Bar: new () => Bar; // Not let or const
}
type FindFoo = FindGlobalInstanceType<'Foo'>; // Doesn't work
type FindBar = FindGlobalInstanceType<'Bar'>; // Works
```
@category Utilities
*/
export type FindGlobalInstanceType<Name extends string> =
Name extends string
? typeof globalThis extends Record<Name, abstract new (...arguments: any[]) => infer T> ? T : never
: never;
56 changes: 29 additions & 27 deletions source/structured-cloneable.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type {TypedArray} from './typed-array';
import type {FindGlobalInstanceType} from './find-global-type';

type StructuredCloneablePrimitive =
| string
Expand All @@ -17,32 +18,34 @@ type StructuredCloneableData =
| Date
| Error
| RegExp
| TypedArray;
// Requires DOM or @types/node
// | Blob
// | File
// DOM exclusive types
// | AudioData
// | CropTarget
// | CryptoKey
// | DOMException
// | DOMMatrix
// | DOMMatrixReadOnly
// | DOMPoint
// | DOMPointReadOnly
// | DOMQuad
// | DOMRect
// | DOMRectReadOnly
// | FileList
// | FileSystemDirectoryHandle
// | FileSystemFileHandle
// | FileSystemHandle
// | GPUCompilationInfo
// | GPUCompilationMessage
// | ImageBitmap
// | ImageData
// | RTCCertificate
// | VideoFrame
| TypedArray
| FindGlobalInstanceType<
// DOM or Node types
| 'Blob'
| 'File'
// DOM exclusive types
| 'AudioData'
| 'CropTarget'
| 'CryptoKey'
| 'DOMException'
| 'DOMMatrix'
| 'DOMMatrixReadOnly'
| 'DOMPoint'
| 'DOMPointReadOnly'
| 'DOMQuad'
| 'DOMRect'
| 'DOMRectReadOnly'
| 'FileList'
| 'FileSystemDirectoryHandle'
| 'FileSystemFileHandle'
| 'FileSystemHandle'
| 'GPUCompilationInfo'
| 'GPUCompilationMessage'
| 'ImageBitmap'
| 'ImageData'
| 'RTCCertificate'
| 'VideoFrame'
>;

type StructuredCloneableCollection =
| readonly StructuredCloneable[]
Expand All @@ -55,7 +58,6 @@ Matches a value that can be losslessly cloned using `structuredClone`.
Note:
- Custom error types will be cloned as the base `Error` type
- This type doesn't include types exclusive to the TypeScript DOM library (e.g. `DOMRect` and `VideoFrame`)
@see https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm
Expand Down
74 changes: 74 additions & 0 deletions test-d/find-global-type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/* eslint-disable no-var, unicorn/prevent-abbreviations */
import {expectAssignable, expectNotAssignable, expectType} from 'tsd';
import type {FindGlobalInstanceType, FindGlobalType} from '..';

declare class NonGlobalES6Class {}
declare var nonGlobalVar: number;
declare let nonGlobalLet: number;
declare const nonGlobalConst: number;

declare global {
class GlobalES6Class {}
var globalVar: string;
let globalLet: number;
const globalConst: number;

type GlobalClass = {foo: string};
var GlobalConstructorVarStyle: new () => GlobalClass;
let GlobalConstructorLetStyle: new () => GlobalClass;
const GlobalConstructorConstStyle: new () => GlobalClass;

type GlobalTypeAlias = {value: string};

var nonConstructorFunction: () => Date;
}

// === FindGlobalType ===

// Success
declare const foundGlobalVar: FindGlobalType<'globalVar'>;
expectType<string>(foundGlobalVar);

// Failures
declare const foundNonGlobalES6Class: FindGlobalType<'nonGlobalVar'>;
expectType<never>(foundNonGlobalVar);
declare const foundNonGlobalVar: FindGlobalType<'nonGlobalVar'>;
expectType<never>(foundNonGlobalVar);
declare const foundNonGlobalLet: FindGlobalType<'nonGlobalLet'>;
expectType<never>(foundNonGlobalLet);
declare const foundNonGlobalConst: FindGlobalType<'nonGlobalConst'>;
expectType<never>(foundNonGlobalConst);

declare const foundGlobalLet: FindGlobalType<'globalLet'>;
expectType<never>(foundGlobalLet);
declare const foundGlobalConst: FindGlobalType<'globalConst'>;
expectType<never>(foundGlobalConst);

// === FindGlobalInstanceType ===

// Success
declare const foundInstanceDate: FindGlobalInstanceType<'Date'>;
expectType<Date>(foundInstanceDate);
declare const foundInstanceMultiple: FindGlobalInstanceType<'Date' | 'Error'>;
expectType<Date | Error>(foundInstanceMultiple);
declare const foundInstanceMultiplePartial: FindGlobalInstanceType<'Date' | 'Error' | 'NonExistentType'>;
expectType<Date | Error>(foundInstanceMultiplePartial);
declare const foundInstanceGlobalConstructorVarStyle: FindGlobalInstanceType<'GlobalConstructorVarStyle'>;
expectType<GlobalClass>(foundInstanceGlobalConstructorVarStyle);

// Failures
declare const foundInstanceNonExistentType: FindGlobalInstanceType<'NonExistentType'>;
expectType<never>(foundInstanceNonExistentType);
declare const foundInstanceNonGlobalES6Class: FindGlobalInstanceType<'NonGlobalES6Class'>;
expectType<never>(foundInstanceNonGlobalES6Class);

declare const foundInstanceGlobalTypeAlias: FindGlobalInstanceType<'GlobalTypeAlias'>;
expectType<never>(foundInstanceGlobalTypeAlias);
declare const foundInstanceGlobalES6Class: FindGlobalInstanceType<'GlobalES6Class'>;
expectType<never>(foundInstanceGlobalES6Class);
declare const foundInstanceGlobalConstructorLetStyle: FindGlobalInstanceType<'GlobalConstructorLetStyle'>;
expectType<never>(foundInstanceGlobalConstructorLetStyle);
declare const foundInstanceGlobalConstructorConstStyle: FindGlobalInstanceType<'GlobalConstructorConstStyle'>;
expectType<never>(foundInstanceGlobalConstructorConstStyle);
declare const foundInstanceGlobalNonConstructorFunction: FindGlobalInstanceType<'nonConstructorFunction'>;
expectType<never>(foundInstanceGlobalNonConstructorFunction);

0 comments on commit 0086cd6

Please sign in to comment.