Giter VIP home page Giter VIP logo

tsafe's Introduction

A collection of utilities to take your TypeScript development up a notch


(you can cherrypick what you import)

Documentation

A few GIFs to convince you that you need tsafe in your life


Assert things you know are true, get runtime errors where you were wrong:



Implement compile time unit testing



Playground



Make sure all properties of an object are deconstructed


Playground



Make sure you never forget a case in a switch


Playground



Make TypeScript believe whatever you say without having to write const obj2 = obj as Bar.
The more powerfully is to be able to tell TypeScript that obj ist not of type Bar:



Make sure your zod schema exactly matches a given type:

Motivations

Powerful TypeScript features like assertion functions or user-defined type guards are only useful if paired with utility functions.

TypeScript, however, only exports type helpers (e.g. Record, ReturnType, etc.).

This module provides «the missing builtins» such as the assert function and corrects frustrating aspects of default utility types such as ReturnType.

Documentation website

Installation

tsafe is both an NPM and a Deno module. (Achieved with denoify)

Import in deno:

import { assert, typeGuard, ... } from "https://deno.land/x/tsafe/mod.ts";

Install elsewhere:

$ npm install --save tsafe
#OR
$ yarn add tsafe

tsafe's People

Contributors

actions-user avatar danieldietrich avatar garronej avatar husseinhere avatar renovate[bot] avatar thieryw avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

tsafe's Issues

Introduce Extends

Hi Joseph,

there is the use-case of testing if only certain TS types cover a special type definition.

// a type that reflects functions and arrow functions
type Fn<A extends any[] = any[], R = any> = (...args: A) => R;

I need to ensure that only and only function cover the Fn type. E.g. type Fn = any would cover functions but also other types that are not intended to be expected. The existing type Equals is unsuitable for testing if types are inside or outside of a domain (read: a set of valid types).

Introducing an Extends type to tsafe would make it possible to assert that Fn covers the types we intend to cover and to ensure that other types are not covered (as expected).

👇👇👇

type Extends<T1, T2> = T1 extends T2 ? true : false;

By having the following unit tests, we would ensure that the Fn type does not cover more types than expected.

describe('type Fn', () => {

    function fn() {}
    
    class A {}

    // "sunny path": these would sill succeed if `type Fn = any`
    it('should cover () => any', () => { tsafeAssert<Extends<() => any, Fn>>(); });
    it('should cover () => void', () => { tsafeAssert<Extends<() => void, Fn>>(); });
    it('should cover (...args: any[]) => any', () => { tsafeAssert<Extends<(...args: any[]) => any, Fn>>(); });
    it('should cover typeof fn', () => { tsafeAssert<Extends<typeof fn, Fn>>(); });

    // "correctness": these would fail if `type Fn = any`
    it('should not cover undefined', () => { tsafeAssert<Not<Extends<undefined, Fn>>>(); });
    it('should not cover null', () => { tsafeAssert<Not<Extends<null, Fn>>>(); });
    it('should not cover boolean', () => { tsafeAssert<Not<Extends<boolean, Fn>>>(); });
    it('should not cover number', () => { tsafeAssert<Not<Extends<number, Fn>>>(); });
    it('should not cover string', () => { tsafeAssert<Not<Extends<string, Fn>>>(); });
    it('should not cover array', () => { tsafeAssert<Not<Extends<any[], Fn>>>(); });
    it('should not cover object', () => { tsafeAssert<Not<Extends<object, Fn>>>(); });
    it('should not cover class', () => { tsafeAssert<Not<Extends<A, Fn>>>(); });

});

If you also think that Extends would be a great fit for tsafe, I would be happy to create a PR!

What do you think?

Thanks!

[Help Requested] Fixing Typos in Documentation

The very start of the [is](https://docs.tsafe.dev/is) function's documentation says:

is is meant to be used in conjonction with assert and enable you to tell the compiler:
"Trust me this `valule` is of type `T`" or "Trust me this `value` is not of type `T`"

This should be:

`is` is meant to be used in conjunction with `assert` and enables you to tell the compiler:
"Trust me, this `value` is of type `T`" or "Trust me this `value` is not of type `T`"

This is one of a few typos I have noticed, as well as missing commas in the import line on assert:

// The website says
import { assert AssertionError } from "tsafe/assert";
// Should say
import { assert, AssertionError } from "tsafe/assert";  

If it is possible, I would be happy to submit a pull request to modify these typos, however I cannot seem to find where these files are stored. If that is not a possibility, I can collect all the typos noticed into a single issue, such as this one, for easy reference and fixing.

Question: Runtime/Build time?

Hello! Love this project. I do have one question. Do these checks only apply when it's runtime? Or, do they apply to build time? If I were to compile it down into just JS, would they still work?

Possibly incorrectly typed objectEntries not returning an array type

Hi! 👋

Firstly, thanks for your work on this project! 🙂

I think that objectEntries is incorrectly typed - it currently returns a union of possible entries, while it should return an array of union of possible entries in order to match the Object.entries() functionality. (note: the actual js implementation is correct though). Also, I think it incorrectly adds undefined to the union of possible entries if at least one of values is optional, as { [Key in keyof O]: [Key, O[Key]]; }[keyof O] doesn't seem to logically allow undefined (nor the resulting array of entries from Object.entries() returns undefined in such case).


Sample code:

const a: { a?: number, b: number, c: number } = ...
const e = objectEntries(a)

type of e:

const e: ["a", (number | undefined)] | ["b", number] | ["c", number] | undefined

type of e after the fix presented below:

 (["a", (number | undefined)] | ["b", number] | ["c", number])[]

Here is the diff that solved my problem:

diff --git a/node_modules/tsafe/objectEntries.d.ts b/node_modules/tsafe/objectEntries.d.ts
index 1f95606..b941470 100644
--- a/node_modules/tsafe/objectEntries.d.ts
+++ b/node_modules/tsafe/objectEntries.d.ts
@@ -1,8 +1,9 @@
 /** https://docs.tsafe.dev/objectentries */
-export declare function objectEntries<O extends Record<string, any>>(o: O): {
+export declare function objectEntries<O extends Record<string, any>>(o: O): NonNullable<{
     [Key in keyof O]: [Key, O[Key]];
-}[keyof O];
+}[keyof O]>[];
+
 /** Return type of objectEntries https://docs.tsafe.dev/objectentries */
-export declare type ObjectEntries<O extends Record<string, any>> = {
+export declare type ObjectEntries<O extends Record<string, any>> = NonNullable<{
     [Key in keyof O]: [Key, O[Key]];
-}[keyof O];
+}[keyof O]>[];

This issue body was partially generated by patch-package.

Use text instead of animated GIFs in the readme?

Benefits:

  • It’s easier to read the actual code that is needed for using tsafe.
  • Animations distract from the remaining content (it’s bad in my case but I know of others that have the same issue).
  • It’s easier to copy-paste the examples.

lab/overwriteReadonlyProp.ts:34:63 Implicit conversion of a 'symbol' to a 'string' will fail at runtime

I am a deno user and recently switched to deno's new vendoring feature.
This means I am no longer using the cached (transpiled .js) version, but rather the .ts source code.

Deno gives me the following error ([email protected]):

TS2731 [ERROR]: Implicit conversion of a 'symbol' to a 'string' will fail at runtime. Consider wrapping this expression in 'String(...)'.
        throw new Error(`Probably a wrong ides to overwrite ${propertyName} getter`);
                                                              ~~~~~~~~~~~~
    at .../raw.githubusercontent.com/garronej/tsafe/v1.0.0/deno_dist/lab/overwriteReadonlyProp.ts:34:63

Equals on enums reports incorrect results

This is currently not working in tsafe:

import type { Equals } from "tsafe";
import { assert } from "tsafe/assert";

export enum RuleObjectType {
  OPPORTUNITY = "OPPORTUNITY",
  QUOTE = "QUOTE",
}
export enum RuleObjectType2 {
  OPPORTUNITY = "OPPORTUNITY",
  QUOTE = "QUOTE",
}

assert<Equals<RuleObjectType, RuleObjectType2>>(true); // returns false, should return true

Dependency Dashboard

This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.

Rate Limited

These updates are currently rate limited. Click on a checkbox below to force their creation now.

  • Update actions/checkout action to v2.4.2
  • Update actions/setup-node action to v2.5.1
  • Update dependency prettier to v2.7.1
  • Update dependency typescript to v4.7.4
  • Update typescript-eslint monorepo to v4.33.0 (@typescript-eslint/eslint-plugin, @typescript-eslint/parser)
  • Update actions/cache action to v3
  • Update actions/checkout action to v3
  • Update actions/setup-node action to v3
  • Update dependency @types/node to v16
  • Update dependency eslint to v8
  • Update dependency eslint-config-prettier to v8
  • Update dependency husky to v8
  • Update dependency lint-staged to v13
  • Update typescript-eslint monorepo to v5 (major) (@typescript-eslint/eslint-plugin, @typescript-eslint/parser)

Open

These updates have all been created already. Click a checkbox below to force a retry/rebase of any.

Detected dependencies

github-actions
.github/workflows/ci.yaml
  • actions/checkout v2.3.4
  • actions/setup-node v2.1.3
  • bahmutov/npm-install v1
  • garronej/ts-ci v1.1.2
  • actions/checkout v2.3.4
  • actions/setup-node v2.1.3
  • bahmutov/npm-install v1
  • actions/checkout v2
  • actions/setup-node v1
  • actions/cache v1
  • denolib/setup-deno master
  • bahmutov/npm-install v1
  • garronej/ts-ci v1.1.2
  • actions/checkout v2
  • actions/setup-node v2.1.3
  • bahmutov/npm-install v1
  • softprops/action-gh-release v1
npm
package.json
  • @types/node ^10.0.0
  • @typescript-eslint/eslint-plugin ^4.15.1
  • @typescript-eslint/parser ^4.15.1
  • eslint ^7.20.0
  • eslint-config-prettier ^7.2.0
  • evt ^2.3.0
  • husky ^4.3.0
  • lint-staged ^10.5.4
  • prettier ^2.2.1
  • typescript ^4.2.3
  • denoify ^0.11.5

  • Check this box to trigger a request for Renovate to run again on this repository

Error identifying equality creating z.custom

I encountered a problem identifying equality between types generated by z.custom on an object

General file
Captura de tela de 2023-11-16 09-19-04

Schema type inferred
Captura de tela de 2023-11-16 09-24-56

Type created
Captura de tela de 2023-11-16 09-24-59

It can be seen that when the schema assert is performed directly it passes without problems. However, when asserting within an object it presents an error even though it has the same typing.

I also noticed that when determining the attribute as optional the error disappears.

Enable to typesafely compose Object.fromEntries and Object.entries

It would be really powerful to be able to apply transformation to an object using the well-known pattern:

const transformedObject= Object.fromEntries(
    Object.entries(originalObject)
        .map(([key, value])=> [key, f(value)]
);

And have TypeScript infers the type of transformedObject based on the transformation applied to the values f(value).

We are half way there already with the utilities:

But the typing of fromEntires() would have to be improved a bit so that the following example would pass:

import { Reflect } from "tsafe/Reflect";
import { assert } from "tsafe/assert";
import type { Equals } from "tsafe";
import { objectEntries } from "tsafe/objectEntries";
import { fromEntires } from "tsafe/objectFromEntries";

const originalObject = Reflect<{
    a: string;
    b: string;
}>();

const modifiedObject = objectFromEntries(
    objectEntries(originalObject)
        .map(([key, value]) => [key, value.length] as const)
);

type ExpectedTypeOfModifiedObject = {
    a: number;
    b: number;
};

assert<Equals<typeof modifiedObject, ExpectedTypeOfModifiedObject>>();

In the current version of tsafe we're getting this:
image

Asserting Two Types Not Throwing Error

Hi there, I've been looking for a way to compare two types at runtime and have stumbled upon your repository.

I have a function that takes a generic type argument and returns a value of that type. This value is retrieved from a firebase server. Firebase does not properly store arrays, so what I'm attempting to do is compare the generic type argument received at runtime and check if it is an array. If so then convert the object received from firebase to an array.

The issue I have run into is that I can't seem to get the assert in combination with Equals to throw a runtime error. For example, if I simply put assert<Equals<{}, []>>, nothing happens. Is this the intended behavior, am I missing something?

assert incorrectly narrows type

This code:

import { assert } from "tsafe";
const x: string | undefined = Math.random() < 0.5 ? undefined : "wtf";
assert(x);
let y: string = x;
console.log(y);

should not compile, but it does. Half the time it will console.log undefined and half the time will console.log wtf. Yet the type of y is supposedly a string.

It's not obvious why, so to avoid the spoiling the fun the explanation is base64 encoded: QXNzZXJ0IGlzIGRlZmluZWQgd2l0aCBgY29uZGl0aW9uOiBhbnkgPSB0cnVlYCB3aGljaCBjb21waWxlcyB0byAiaWYgY29uZGl0aW9uIGlzIHVuZGVmaW5lZCwgbWFrZSBpdCB0cnVlIiB3aGlsZSB0aGUgdHlwZSBzaWduYXR1cmUgZG9lc24ndCByZWZsZWN0IHRoaXMuIA==

Ability to trigger warnings instead of errors

Hi, thank you a lot for this great library.

I don't know if it's doable but it would be so great to be able to trigger warnings instead of errors.

One example of a use case is that I check that I handled all the cases, thereby in the future if I add a case, I don't forget to handle it in this piece of code. But when I create a case, I may want to test other parts of the code before handling it here.

const foo: a | b | c = ...;
if (isA(foo)) { ... }
else if (isB(foo)) { ... }
else if (isC(foo)) { ... }
else {
  // we check that we handled all the cases, thereby if we add a case in the future, we will trigger an error
  assert<Equals<typeof foo, never>>();
}

`tsafe` has improper esm exports

import { assert } from "tsafe" does not work in latest versions

Last known working point (is not the bisection point, might be working on later versions): 1.2.1

@garronej

If you need minimum repro, let me know

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.