vue.js - Έλεγχος των props στο Vue 3 με Typescript τόσο κατά τη μεταγλώττιση όσο και κατά την εκτέλεση
Η ομάδα μου κατασκευάζει μια βιβλιοθήκη στοιχείων Vue 3. Καθώς το έργο επεκτείνεται σε μεγαλύτερη κλίμακα, το αναδιατάξαμε χρησιμοποιώντας TypeScript για να έχουμε καλύτερη εμπειρία ανάπτυξης.
Αλλά να τι συμβαίνει - υπάρχουν ομάδες που χρησιμοποιούν τη βιβλιοθήκη μας σε διάφορα περιβάλλοντα, με ή χωρίς typescript και bundler, πράγμα που σημαίνει ότι για σχεδόν κάθε component πρέπει να επικυρώνουμε τους τύπους ιδιοτήτων τόσο κατά το χρόνο σύνταξης όσο και κατά το χρόνο εκτέλεσης.
props: {
someProp: {
type: String as PropType<'enum1' | 'enum2' | 'enum3'>,
validator: (v: string) => {
return ['enum1', 'enum2', 'enum3'].includes(v)
}
}
}
Ο κώδικας τρέχει καλά. Οι ομάδες που χρησιμοποιούν typescript είναι χαρούμενες - έχουν την αυτόματη ολοκλήρωση του κώδικά τους και μπορούν να εντοπίσουν πιθανά λάθη εκ των προτέρων. Οι ομάδες που χρησιμοποιούν javascript είναι χαρούμενες - μπορούν να ανακαλύψουν οποιοδήποτε σφάλμα σχετικό με τύπους κατά την εκτέλεση χάρη στον validator. Αλλά εμείς δεν είμαστε χαρούμενοι, αφού ο κώδικας είναι επαναλαμβανόμενος. Τεχνικά <'enum1' | ...> είναι ορισμός τύπου και ['enum1', ...] είναι πίνακας, δεν είναι το ίδιο πράγμα - αλλά κυριολεκτικά γράφουμε την ίδια συμβολοσειρά πολλές φορές.
Υπάρχει κάποιο τρόπος να υλοποιήσω κάτι που μπορεί να ορίσει ένα prop με PropType και validator χωρίς να χρειάζεται να επαναλαμβάνω τον εαυτό μου;
Απάντηση #1
Λοιπόν, αυτή τη στιγμή κάνω κάτι τέτοιο:
export const buildProp = <
T = any,
R extends boolean = boolean,
D extends T = T
>({
type?: any,
/* all the values allowed for this prop */
values?: readonly T[],
required?: R,
/* if a prop is required, it shouldn't default to any value */
/* if a prop has a default value, it shouldn't be marked as required */
defaultValue?: R extends true
? never
: D extends Record<string, unknown> | Array<any>
? () => D
: D,
/* any other validations */
validator?: (val: any) => boolean
} = {}) => {
return {
type: type as PropType<T | never>,
required: !!required,
default: defaultValue,
validator: (val: any) => {
let valid = false
// we see if the prop value passed in is in the union
// or is the defaultValue
if (values) {
valid =|| [...values, defaultValue].includes(val)
}
if (validator) {
valid =|| validator(val)
}
return valid
}
}
}
Ως αποτέλεσμα,
someProp: buildProp({
type: String,
values: ['enum1', 'enum2', 'enum3']
})
θα παράγει
someProp: {
type: String as PropType<'enum1' | 'enum2' | 'enum3'>,
validator: (val) => {
return ['enum1', 'enum2', 'enum3'].includes(val)
}
}
if (props.someProp === 'illegalValue') {
// ERR: will always return false since type "'enum1' | 'enum2' | 'enum3'" has no overlap with "illegalValue"
}
