Skip to content

Commit

Permalink
chainable: add a few string chainable methods
Browse files Browse the repository at this point in the history
  • Loading branch information
gvergnaud committed May 9, 2023
1 parent 0b92728 commit da93b9a
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 11 deletions.
65 changes: 57 additions & 8 deletions src/patterns.ts
Original file line number Diff line number Diff line change
Expand Up @@ -593,19 +593,68 @@ export const any = when(isUnknown);
export const _ = any;

/**
* `P.string` is a wildcard pattern matching any **string**.
* `P.string.startsWith(start)` is a pattern, matching **strings** starting with `start`.
*
* [Read documentation for `P.string.startsWith` on GitHub](https://github.com/gvergnaud/ts-pattern#PstringstartsWith)
*
* @example
* match(value)
* .with(P.string.startsWith('A'), () => 'value starts with an A')
*/

const startsWith = <input, const start extends string>(
start: start
): GuardExcludeP<input, `${start}${string}`, never> =>
when((value) => isString(value) && value.startsWith(start));

/**
* `P.string.endsWith(end)` is a pattern, matching **strings** ending with `end`.
*
* [Read documentation for `P.string.endsWith` on GitHub](https://github.com/gvergnaud/ts-pattern#PstringendsWith)
*
* @example
* match(value)
* .with(P.string.endsWith('!'), () => 'value ends with an !')
*/
const endsWith = <input, const end extends string>(
end: end
): GuardExcludeP<input, `${string}${end}`, never> =>
when((value) => isString(value) && value.endsWith(end));

/**
* `P.string.includes(substr)` is a pattern, matching **strings** containing `substr`.
*
* [Read documentation for `P.string.includes` on GitHub](https://github.com/gvergnaud/ts-pattern#Pstringincludes)
*
* @example
* match(value)
* .with(P.string.includes('http'), () => 'value contains http')
*/
const includes = <input, const substr extends string>(
substr: substr
): GuardExcludeP<input, string, never> =>
when((value) => isString(value) && value.includes(substr));

const assignStringMethods = <p extends GuardP<any, any>>(pattern: p) =>
Object.assign(pattern, {
startsWith,
endsWith,
includes,
});

/**
* `P.string` is a wildcard pattern, matching any **string**.
*
* [Read documentation for `P.string` on GitHub](https://github.com/gvergnaud/ts-pattern#Pstring-wildcard)
*
* @example
* match(value)
* .with(P.string, () => 'will match on strings')
*/

export const string = when(isString);
export const string = assignStringMethods(when(isString));

/**
* `P.number` is a wildcard pattern matching any **number**.
* `P.number` is a wildcard pattern, matching any **number**.
*
* [Read documentation for `P.number` on GitHub](https://github.com/gvergnaud/ts-pattern#Pnumber-wildcard)
*
Expand All @@ -616,7 +665,7 @@ export const string = when(isString);
export const number = when(isNumber);

/**
* `P.boolean` is a wildcard pattern matching any **boolean**.
* `P.boolean` is a wildcard pattern, matching any **boolean**.
*
* [Read documentation for `P.boolean` on GitHub](https://github.com/gvergnaud/ts-pattern#boolean-wildcard)
*
Expand All @@ -626,7 +675,7 @@ export const number = when(isNumber);
export const boolean = when(isBoolean);

/**
* `P.bigint` is a wildcard pattern matching any **bigint**.
* `P.bigint` is a wildcard pattern, matching any **bigint**.
*
* [Read documentation for `P.bigint` on GitHub](https://github.com/gvergnaud/ts-pattern#bigint-wildcard)
*
Expand All @@ -636,7 +685,7 @@ export const boolean = when(isBoolean);
export const bigint = when(isBigInt);

/**
* `P.symbol` is a wildcard pattern matching any **symbol**.
* `P.symbol` is a wildcard pattern, matching any **symbol**.
*
* [Read documentation for `P.symbol` on GitHub](https://github.com/gvergnaud/ts-pattern#symbol-wildcard)
*
Expand All @@ -646,7 +695,7 @@ export const bigint = when(isBigInt);
export const symbol = when(isSymbol);

/**
* `P.nullish` is a wildcard pattern matching **null** or **undefined**.
* `P.nullish` is a wildcard pattern, matching **null** or **undefined**.
*
* [Read documentation for `P.nullish` on GitHub](https://github.com/gvergnaud/ts-pattern#nullish-wildcard)
*
Expand Down
45 changes: 42 additions & 3 deletions tests/chainable.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,49 @@
import { P, match } from '../src';
import { Equal, Expect } from '../src/types/helpers';

describe('chainable methods', () => {
describe('string', () => {
it(`P.string.includes('str')`, () => {});
it(`P.string.startsWith('str')`, () => {});
it(`P.string.endsWith('str')`, () => {});
it(`P.string.includes('str')`, () => {
const f = (input: string | number) =>
match(input)
.with(P.string.includes('!!'), (value) => {
type t = Expect<Equal<typeof value, string>>;
return 'includes !!';
})
.otherwise(() => 'something else');

expect(f('hello!!')).toBe('includes !!');
expect(f('nope')).toBe('something else');
});

it(`P.string.startsWith('str')`, () => {
const f = (input: string | number) =>
match(input)
.with(P.string.startsWith('hello '), (value) => {
type t = Expect<Equal<typeof value, `hello ${string}`>>;
return 'starts with hello';
})
.otherwise(() => 'something else');

expect(f('hello gabriel')).toBe('starts with hello');
expect(f('gabriel')).toBe('something else');
});

it(`P.string.endsWith('str')`, () => {
const f = (input: string | number) =>
match(input)
.with(P.string.endsWith('!!'), (value) => {
type t = Expect<Equal<typeof value, `${string}!!`>>;
return 'ends with !!';
})
.otherwise(() => 'something else');

expect(f('hello!!')).toBe('ends with !!');
expect(f('nope')).toBe('something else');
});
it(`P.string.regex('[a-z]+')`, () => {});
});

describe('number', () => {
it(`P.number.between(1, 10)`, () => {});
it(`P.number.lt(12)`, () => {});
Expand Down

0 comments on commit da93b9a

Please sign in to comment.