Skip to content

Commit

Permalink
Add safeCastTo() (#63)
Browse files Browse the repository at this point in the history
Co-authored-by: Sindre Sorhus <[email protected]>
  • Loading branch information
Max10240 and sindresorhus authored Aug 23, 2024
1 parent 32af6a3 commit d2d4368
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 0 deletions.
1 change: 1 addition & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {isDefined} from 'ts-extras';
**General**

- [`asWritable`](source/as-writable.ts) - Cast the given value to be [`Writable`](https://github.com/sindresorhus/type-fest/blob/main/source/writable.d.ts).
- [`safeCastTo`](source/safe-cast-to.ts) - Cast a value to the given type safely.

**Type guard**

Expand Down
1 change: 1 addition & 0 deletions source/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export {safeCastTo} from './safe-cast-to.js';
export {arrayIncludes} from './array-includes.js';
export {asWritable} from './as-writable.js';
export {assertError} from './assert-error.js';
Expand Down
29 changes: 29 additions & 0 deletions source/safe-cast-to.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/**
Cast a value to the given type safely.
The `as` keyword allows unsafe conversions between unrelated types, like converting a number to a string. This function restricts casting to compatible types, preserving type safety.
@example
```
type Foo = {
a: string;
b?: number;
};
declare const possibleUndefined: Foo | undefined;
const foo = possibleUndefined ?? safeCastTo<Partial<Foo>>({});
console.log(foo.a ?? '', foo.b ?? 0);
const bar = possibleUndefined ?? {};
// @ts-expect-error
console.log(bar.a ?? '', bar.b ?? 0);
// ^^^ Property 'a' does not exist on type '{}'.(2339)
// ^^^ Property 'b' does not exist on type '{}'.(2339)
```
@category General
*/
export function safeCastTo<T>(value: T): T {
return value;
}
38 changes: 38 additions & 0 deletions test/safe-cast-to.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/* eslint-disable @typescript-eslint/ban-types */
import test from 'ava';
import {expectTypeOf} from 'expect-type';
import {safeCastTo} from '../source/index.js';

test('safeCastTo()', t => {
type Foo = {
a: string;
b?: number;
};

const EmptyObject = {};
const foo: Foo = {
a: '',
b: 0,
};

t.is(EmptyObject, safeCastTo(EmptyObject));
t.is(foo, safeCastTo(foo));

expectTypeOf({}).toEqualTypeOf<{}>();
expectTypeOf(safeCastTo({})).toEqualTypeOf<{}>();
expectTypeOf({}).not.toEqualTypeOf<Partial<Foo>>();
expectTypeOf(safeCastTo({})).not.toEqualTypeOf<Partial<Foo>>();
expectTypeOf(safeCastTo<Partial<Foo>>({})).toEqualTypeOf<Partial<Foo>>();
expectTypeOf(safeCastTo<Partial<Foo>>({}).a).toEqualTypeOf<string | undefined>();
expectTypeOf(safeCastTo<Partial<Foo>>({}).b).toEqualTypeOf<number | undefined>();

expectTypeOf(foo).toEqualTypeOf<Foo>();
expectTypeOf(safeCastTo(foo)).toEqualTypeOf<Foo>();
expectTypeOf(safeCastTo<Partial<Foo>>(foo)).not.toEqualTypeOf<Foo>();
expectTypeOf(safeCastTo<Partial<Foo>>(foo)).toEqualTypeOf<Partial<Foo>>();

// @ts-expect-error
safeCastTo<Foo>({});
// @ts-expect-error
safeCastTo<Required<Foo>>(foo);
});

0 comments on commit d2d4368

Please sign in to comment.