Skip to content

Commit

Permalink
feat(typings): use DeepReadonly generic
Browse files Browse the repository at this point in the history
  • Loading branch information
Heymdall committed Jun 18, 2018
1 parent 8fcc5a8 commit 17c6f8b
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 49 deletions.
100 changes: 57 additions & 43 deletions typings/__tests__/__snapshots__/index.tests.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -5,90 +5,104 @@ exports[`simple component with all prop types 1`] = `
import { Component, ReactNode } from 'react';
import * as Type from 'prop-types';
type Primitive = string | number | boolean | undefined | null;
type DeepReadonly<T> =
T extends Primitive ? T :
T extends Array<infer U> ? DeepReadonlyArray<U> :
DeepReadonlyObject<T>;
interface DeepReadonlyArray<T> extends ReadonlyArray<DeepReadonly<T>> { }
type DeepReadonlyObject<T> = {
readonly [P in keyof T]: DeepReadonly<T[P]>
};
export type AOptionalEnumFieldType = 'News' | 'Photos';
export type ARequiredEnumFieldType = 'News' | 'Photos';
export type AOptionalUnionFieldType = string | number;
export type ARequiredUnionFieldType = string | number;
export type AOptionalObjectOfFieldType = {
readonly [key: string]: number;
[key: string]: number;
};
export type ARequiredObjectOfFieldType = {
readonly [key: string]: number;
[key: string]: number;
};
export type AOptionalObjectWithShapeSubShapeFieldType = {
/**
* Even deeper documentation
*/
readonly name?: string;
readonly size?: number
name?: string;
size?: number
};
export type AOptionalObjectWithShapeFieldType = {
readonly color?: string;
color?: string;
/**
* Sub prop documentation
*/
readonly fontSize: number;
fontSize: number;
/**
* @param {string} value
*/
readonly onChange?: Function;
readonly subShape?: AOptionalObjectWithShapeSubShapeFieldType
onChange?: Function;
subShape?: AOptionalObjectWithShapeSubShapeFieldType
};
export type AOnClickReturnFieldType = string | number;
export type APublicWithParamsUnionParamFieldType = string | number;
export interface AProps {
readonly optionalArray?: ReadonlyArray<any>;
readonly requiredArray: ReadonlyArray<any>;
export type AProps = DeepReadonlyObject<{
optionalArray?: any[];
requiredArray: any[];
/**
* Prop documentation
*/
readonly optionalBool?: boolean;
readonly requiredBool: boolean;
readonly optionalFunc?: Function;
readonly requiredFunc: Function;
readonly optionalNumber?: number;
readonly requiredNumber: number;
readonly optionalObject?: object;
readonly requiredObject: object;
readonly optionalString?: string;
readonly requiredString: string;
readonly optionalSymbol?: Symbol;
readonly requiredSymbol: Symbol;
readonly optionalNode?: ReactNode;
readonly requiredNode: ReactNode;
readonly optionalElement?: ReactNode;
readonly requiredElement: ReactNode;
readonly optionalMessage?: any/* Не нашёлся встроенный тип для типа {\\"name\\":\\"instanceOf\\",\\"value\\":\\"Message\\"}
optionalBool?: boolean;
requiredBool: boolean;
optionalFunc?: Function;
requiredFunc: Function;
optionalNumber?: number;
requiredNumber: number;
optionalObject?: object;
requiredObject: object;
optionalString?: string;
requiredString: string;
optionalSymbol?: Symbol;
requiredSymbol: Symbol;
optionalNode?: ReactNode;
requiredNode: ReactNode;
optionalElement?: ReactNode;
requiredElement: ReactNode;
optionalMessage?: any/* Не нашёлся встроенный тип для типа {\\"name\\":\\"instanceOf\\",\\"value\\":\\"Message\\"}
* https://github.com/alfa-laboratory/library-utils/issues/new
*/;
readonly requiredMessage: any/* Не нашёлся встроенный тип для типа {\\"name\\":\\"instanceOf\\",\\"value\\":\\"Message\\"}
requiredMessage: any/* Не нашёлся встроенный тип для типа {\\"name\\":\\"instanceOf\\",\\"value\\":\\"Message\\"}
* https://github.com/alfa-laboratory/library-utils/issues/new
*/;
readonly optionalEnum?: AOptionalEnumFieldType;
readonly requiredEnum: ARequiredEnumFieldType;
readonly optionalUnion?: AOptionalUnionFieldType;
readonly requiredUnion: ARequiredUnionFieldType;
readonly optionalArrayOf?: ReadonlyArray<number>;
readonly requiredArrayOf: ReadonlyArray<number>;
readonly optionalObjectOf?: AOptionalObjectOfFieldType;
readonly requiredObjectOf: ARequiredObjectOfFieldType;
readonly optionalAny?: any;
readonly requiredAny: any;
readonly optionalObjectWithShape?: AOptionalObjectWithShapeFieldType;
optionalEnum?: AOptionalEnumFieldType;
requiredEnum: ARequiredEnumFieldType;
optionalUnion?: AOptionalUnionFieldType;
requiredUnion: ARequiredUnionFieldType;
optionalArrayOf?: ReadonlyArray<number>;
requiredArrayOf: ReadonlyArray<number>;
optionalObjectOf?: AOptionalObjectOfFieldType;
requiredObjectOf: ARequiredObjectOfFieldType;
optionalAny?: any;
requiredAny: any;
optionalObjectWithShape?: AOptionalObjectWithShapeFieldType;
/**
* Callback with documentation
*/
readonly onClick?: (stringParam?: string, count?: number, event?: React.MouseEvent<any>, anotherEvent?: React.KeyboardEvent<any>, element?: HTMLDivElement) => AOnClickReturnFieldType;
readonly onChange?: Function;
onClick?: (stringParam?: string, count?: number, event?: React.MouseEvent<any>, anotherEvent?: React.KeyboardEvent<any>, element?: HTMLDivElement) => AOnClickReturnFieldType;
onChange?: Function;
}
}>;
export type APropTypes = Record<keyof AProps, Type.Validator<AProps>>;
Expand Down
28 changes: 22 additions & 6 deletions typings/stringify-component-definition.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ function stringifyType(type, componentName, propName, typeRefs) {
return 'boolean';
case 'array':
case 'Array':
return 'ReadonlyArray<any>';
return 'any[]';
case 'symbol':
return 'Symbol';
case 'node':
Expand Down Expand Up @@ -115,7 +115,7 @@ function stringifyField(fieldName, type, componentName, propName, typeRefs) {
const typeDescription = stringifyType(type, componentName, `${propName}${upperCamelCase(fieldName)}`, typeRefs);
return (
stringifyDescription(type.description, type.docblock) + // eslint-disable-line prefer-template
`readonly ${fieldName}${type.required ? '' : '?'}: ${typeDescription}`
`${fieldName}${type.required ? '' : '?'}: ${typeDescription}`
);
}

Expand All @@ -134,7 +134,7 @@ function stringifyShape(type, componentName, propName, typeRefs) {
function stringifyObjectOf(type, componentName, propName, typeRefs) {
const fieldType = type.value;
return `{
readonly [key: string]: ${stringifyType(fieldType, componentName, propName, typeRefs)};
[key: string]: ${stringifyType(fieldType, componentName, propName, typeRefs)};
}`;
}

Expand All @@ -145,22 +145,36 @@ function stringifyClassMethod(type, componentName, typeRefs) {
return `${description}${type.name}${typeDef};`;
}

const DEPP_READONLY_TYPES = `
type Primitive = string | number | boolean | undefined | null;
type DeepReadonly<T> =
T extends Primitive ? T :
T extends Array<infer U> ? DeepReadonlyArray<U> :
DeepReadonlyObject<T>;
interface DeepReadonlyArray<T> extends ReadonlyArray<DeepReadonly<T>> {}
type DeepReadonlyObject<T> = {
readonly [P in keyof T]: DeepReadonly<T[P]>
};
`;

function stringifyComponentDefinition(info) {
const typeRefs = []; // PropType fields typedefs
const propsInterfaceName = `${info.displayName}Props`;
const propTypesTypeName = `${info.displayName}PropTypes`;
const propsDef = (
/* eslint-disable indent, object-curly-newline */
`
export interface ${propsInterfaceName} {
export type ${propsInterfaceName} = DeepReadonlyObject<{
${Object.keys(info.props).map((propName) => {
const { required, type, description, docblock } = info.props[propName];
const typeDef = stringifyType(type, info.displayName, propName, typeRefs);
const descriptionString = stringifyDescription(description, docblock);
return `${descriptionString}readonly ${propName}${required ? '' : '?'}: ${typeDef};\n`;
return `${descriptionString}${propName}${required ? '' : '?'}: ${typeDef};\n`;
}).join('')}
}
}>;
`
/* eslint-enable indent, object-curly-newline */
);
Expand All @@ -172,6 +186,8 @@ function stringifyComponentDefinition(info) {
`
import { Component, ReactNode } from 'react';
import * as Type from 'prop-types';
${DEPP_READONLY_TYPES}
${typeRefs.join('\n')}
Expand Down

0 comments on commit 17c6f8b

Please sign in to comment.