Branding Drafts on Type level

Ouverte
#1,232 0 commentaires 0 réactions 0 personnes assignées Voir sur GitHub

Les mainteneurs répondent en général sous 4 jours

Personne n'a encore pris cette issue.

Évaluation

Difficulté
5/5
Temps estimé
Plus d'une semaine
Accessibilité débutants
35/100
Type d'issue
Fonctionnalité
Clarté
À clarifier
Activité
Calme
Stack technique
typescript

Piste de recherche

Commencez par la preuve de concept TypeScript Playground et comparez son comportement concernant branded Draft, MaybeDraft, current, isDraft et produce avec les signatures de types existantes d'Immer. L'issue ne nomme aucun fichier ni test du dépôt ; sa réalisation nécessiterait de décider d'un commun accord si le branding à la compilation est souhaitable et de définir le périmètre de l'implémentation.

Rédigé par le modèle d'indexation à partir du texte de l'issue.

Description

question

🙋‍♂ Question

I was wondering if it would be possible to not just brand at runtime, but branded type at compile time as well?

Cause

I was using redux/toolkit, which uses Immer and had an issue where current(val) was called on a non draft, typechecker was fine, executed it in development, all good, came to production, tried to unwrap a non draft and crashed. Not a big deal, just use isDraft, but it was something technically avoidable if....

Implementation

I could make a PR, I just wanted to figure out if that is even desirable since I assume this question came up already some time ago and was decided against it for a reason to me currently unknown (except maybe that some types are already relatively complex)

Proof of concept

I made a small proof of concept on typescript playground relative quickly to just check out the branding, the idea would be that produce etc return Drafted<T> instead of T hiding a potential source for errors (naming still up for discussion)

I made a small code example on TS playground where I do not duplicate the objects, Proxy them etc for simplicity. If no link should be used, here the snipped:

const brand : unique symbol = Symbol("foobar")

type Draft<T> = T & {[brand]:never}
type MaybeDraft<T> = Draft<T> | T

const a = 32;
const b = {
    a: 32,
    b: "Hello world"
}
const c = [1, 2, 3];
const d = [a,b,c] as const

const draft = <T extends object>(val: T)=> {
    const clone = val
    Object.defineProperty(clone, brand, {configurable: false, enumerable: false, writable: false, value: true})
    return clone as Draft<T>
}


const isDraft = <T extends unknown>(val: T): val is Draft<T> => {
    //@ts-ignore
    if (val[brand]) {
        return true
    }

    return false
}


const B = draft(b)
const C = draft(c)
const D = draft(d)


console.log("A: ", isDraft(a))
console.log("B: ", isDraft(B))
console.log("C: ", isDraft(C))
console.log("D: ", isDraft(D))


/**
 * Accepts a Draft or a Value, but differentiates them on a type level to enforce handling of each of the 2 scenarios
 */
const doSomething = (value: MaybeDraft<number[]>) => {
    //const val = current(value) // Error because value could also not be a draft
    const val = isDraft(value) ? current(value) : value
    return val
}

/**
 * Accepts a Draft or a Value, it doesn't care if it might be a Draft and just uses it
 */
const doSomething2 = (value: number[]) => {
    return value
}

/**
 * Accepts only a Draft, non Drafts are refused on a Type Level at compile time
 */
const current = <T extends object>(value: Draft<T>):T => {
    return value
}

doSomething(C)
doSomething2(C)
//const neverHappend = current(c)
const val = current(C)
Langage dominant
JavaScript
Étoiles
29k
Forks
905
Merge moyen
3 j 16 h
PR mergées (30 j)
22

Préparer son environnement

Ce projet ne fournit ni conteneur de développement, ni Dockerfile, ni guide de contribution : l'installation est à votre charge. Commencez par son README, et consultez notre guide de la première contribution pour les étapes générales.

Par où commencer

  1. Lisez l'issue en entier, puis le guide de contribution du projet.
  2. Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
  3. Forkez le dépôt et travaillez sur une branche.
  4. Ouvrez une pull request qui référence le numéro de l'issue.

Autres issues de immerjs/immer

Toutes les issues de immerjs/immer

Issues similaires

Plus d'issues JavaScript

Recevez les nouvelles issues par e-mail

Un résumé court des issues GitHub adaptées aux débutants.