συνδιακύμανση - Πώς να εκφράσετε έναν τύπο «μόνο για ανάγνωση εγγραφής» στο TypeScript που είναι συνδιακυμαινόμενος με συμβατές διεπαφές;


Πρέπει να ορίσω μια μέθοδο που σειριοποιεί αντικείμενα για αποθήκευση, αλλά η σημασιολογία της απαιτεί να μπορεί να λειτουργεί μόνο με αντικείμενα των οποίων τα κλειδιά των πεδίων είναι τύπου συμβολοσειράς (γιατί πρέπει να ταιριάζουν με τα ονόματα των στηλών, οπότε δεν μπορούν να είναι αριθμοί).

Η χρήση ενός τύπου Record<string, unknown> δεν θα λειτουργήσει για αυτήν την είσοδο του σειριοποιητή, γιατί του παρέχω αντικείμενα που έχουν συγκεκριμένα ορισμένα πεδία (όπως interface Foo {foo: number, bar: string}) και αυτά δεν μπορούν να εκχωρηθούν σε Record<string, unknown>.

Το ίδιο πράγμα, γιατί δεν μπορούμε να αναθέσουμε με ασφάλεια μια List<Cat> σε μια List<Animal> αν η List είναι μεταβλητή, αλλά μπορούμε αν είναι αμετάβλητες.

Οπότε χρειάζεται είτε να προσδιορίσω ότι η παράμετρος τύπου γενικού τύπου keyof T είναι υποσύνολο του string είτε να έχω ένα ReadOnlyRecord<string, unknown> που είναι συνδιαστικό με οποιοδήποτε interface που έχει κλειδιά τύπου string.

Προτάσεις;

Απάντηση #1

Δεν χρειάζεται να λάβετε υπόψη τη διακύμανση εδώ. Ο λόγος που το Record<string, unknown> αποτυγχάνει είναι απλώς επειδή το βοηθητικό εργαλείο απαιτεί ο τύπος παραμέτρου του να έχει υπογραφή δείκτη. Για τον ίδιο λόγο, η μέθοδος δεν μπορεί να περιμένει να λάβει έναν τύπο που μπορεί να ανατεθεί σε { [x:string]: unknown } ως μοναδική παράμετρο της.

Σε κάθε περίπτωση, δεν νομίζω ότι θα μπορέσετε να το κάνετε αυτό χωρίς παραμέτρους τύπου γενικού τύπου. Τότε απλώς χρειάζεται να ελέγξετε αν ένας τύπος με κλειδί μόνο αλφαριθμητικό, όπως { [P in Extract<keyof T, string> : any }, είναι αναθέσιμος στον τύπο που έχει περαστεί (το αντίστροφο προφανώς θα περνά πάντα καθώς οι διεπαφές σας είναι υποτύποι των πρώτων).

Σημειώστε ότι το X extends T ? T : never στην παράμετρο είναι απαραίτητο για να μπορεί ο μεταγλωττιστής να συναγάγει το T από τη χρήση ενώ διατηρείται ο περιορισμός. Το μόνο μειονέκτημα είναι ότι δεν θα μπορείτε να εντοπίσετε ιδιότητες symbol (αλλά θα μπορείτε αν ο τύπος τους είναι unique symbol):

{
    class Bar {
        public baz<T extends object>(serializable: { [P in Extract<keyof T,string>] : any } extends T ? T : never) { return JSON.stringify(serializable); }
    }

    const foo: Foo = { foo: 42, bar: "answer" };

    const nope = { [1]: true };

    const symb = { [Symbol("ok")]: 24 };

    const US: unique symbol = Symbol("unique");

    const usymb = { [US]: Infinity };

    const bar = new Bar();
    bar.baz(foo); //OK
    bar.baz(nope); //Argument of type '{ 1: boolean; }' is not assignable to parameter of type 'never'
    bar.baz(symb); //OK? Inferred as `{ [x: string]: number; }`
    bar.baz(usymb); //Argument of type '{ [US]: number; }' is not assignable to parameter of type 'never'
}

Παιδική χαρά