Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Not<string, 'not me'> #417

Open
fregante opened this issue Jul 6, 2022 · 4 comments
Open

Add Not<string, 'not me'> #417

fregante opened this issue Jul 6, 2022 · 4 comments

Comments

@fregante
Copy link
Collaborator

fregante commented Jul 6, 2022

I was looking for a way to implement “any string that doesn’t start with a slash” with template literals, then I realized that it can be achieved with a generic Not type:

type Unslashed = Not<string, `/${string}`>
const valid: Unslashed = 'home'
const invalid: Unslashed = '/home'

Is there anything like Not?

Edit: related:

Upvote & Fund

  • We're using Polar.sh so you can upvote and help fund this issue.
  • The funding will be given to active contributors.
  • Thank you in advance for helping prioritize & fund our backlog.
Fund with Polar
@fregante fregante changed the title Add `Not<string, 'not me'> Add Not<string, 'not me'> Jul 6, 2022
@sindresorhus
Copy link
Owner

I think it could be useful.

If only TS had negated types: microsoft/TypeScript#29317

@fregante
Copy link
Collaborator Author

fregante commented Jul 8, 2022

Seen in https://stackoverflow.com/questions/66354585/typescript-type-for-any-string-other-than-a-specific-string-literal

I can't make it work as a reusable type:

type Not<Yes, Not> = Yes extends Not ? never : Yes;
type NotHello = Not<string, 'hello'>;
const notHello: NotHello = 'hello';
// No error because `NotHello === string`

It's possible that TypeScript "forgets" the negation too early

https://www.typescriptlang.org/play?#code/C4TwDgpgBAcg9sAPATQgZwDSwQPigXilTSggA9gIA7AExPmCgH4oqIA3CAJygC4j0AbgBQoSNmAAJCABsZcAhMRpgXAJZUA5lgDkAC1nydOEQGM4VFawTS5cfg1vzF+w3B2CgA


The generic-based works but it's buggy with variables:

type Not<Yes, Not> = Yes extends Not ? never : Yes;
type NotHello<S extends string> = Not<S, 'hello'>;

export const join = <S extends string>(
	...parts: Array<NotHello<S> | number>
): string => parts.join(',');
join('hello'); // Error 🎉 
join('hello', 'world'); // Error 🎉 

let nonConstString = 'sup'
join('hello', nonConstString); // No error 😰 

https://www.typescriptlang.org/play?ts=4.6.4#code/C4TwDgpgBAcg9sAPATQgZwDSwQPigXilTSggA9gIA7AExPmCgH4oqIA3CAJygC4j0AbgCwAKFCRswABIQANnLiIAyqQrU6UNMC4BLKgHM8hBiqwByABbzF5nCNFjyYOF0YBjOFW1QAVnH0CKBU1SloSbT1DHAAKMQBIADpksABDNzR+AEEuLlSQRAZZBSVlPAAfVgBXAFsAI24cMQBKfkj9AwI8NIzE-30Y8wxzZod+qkHrEpHBKAB6OagAUVzXKEAeDcBI-agxccmbOCGocwB3VzkaGfnFla41rZ3HUTkIRiovAGEvbWUdDqDzGgqmBzLsAhMrAcju8qF9vMBflEDKNrthSKseIBeDcADHtQIA

fregante added a commit to refined-github/refined-github that referenced this issue Jul 8, 2022
@kidonng
Copy link
Contributor

kidonng commented Jul 8, 2022

The generic-based works but it's buggy with variables

They needs to be as const or types will be widened.

Checkout the following code on TS Playground:

type Not<Yes, Not> = Yes extends Not ? never : Yes;
type NotHello<S extends string> = Not<S, 'hello'>;

export const join = <S extends string>(
	...parts: Array<NotHello<S> | number>
): string => parts.join(',');
join('hello'); // Error 🎉 
join('hello', 'world'); // Error 🎉 

let nonConstString = 'sup' as const
join('hello', nonConstString); // Error 🎉 

@tommy-mitchell
Copy link
Contributor

Could microsoft/TypeScript#51865 help solve this issue?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants