Mi equipo está construyendo una biblioteca de componentes Vue 3. A medida que el proyecto crece en escala, lo refactorizamos con mecanografiado para obtener una mejor experiencia de desarrollo.
Pero aquí está la cuestión: hay equipos que usan nuestra lib en diferentes entornos, con o sin mecanografiado y empaquetador, lo que significa que para casi todos los componentes necesitamos validar los tipos de accesorios tanto en tiempo de compilación como en tiempo de ejecución.
props: { someProp: { type: String as PropType<'enum1' | 'enum2' | 'enum3'>, validator: (v: string) => { return ['enum1', 'enum2', 'enum3'].includes(v) } } } El código funciona bien. Los equipos que usan mecanografiado están contentos: completaron su código y pueden detectar posibles errores con anticipación. Los equipos que usan javascript están contentos: pueden descubrir cualquier error relacionado con el tipo en tiempo de ejecución gracias al validator . Pero no estamos contentos, ya que el código está duplicado. Técnicamente <'enum1' | ...> es una definición de tipo y ['enum1', ...] es una matriz, no son lo mismo, pero literalmente escribimos la misma cadena varias veces.
¿Hay alguna forma de implementar algo que pueda definir un accesorio con PropType y validator sin tener que repetirme?
Bueno, actualmente estoy haciendo algo como esto:
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 } } }Como resultado,
someProp: buildProp({ type: String, values: ['enum1', 'enum2', 'enum3'] })Producirá
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" }