Types refactoring
parent
edc8f4eb6a
commit
beb5c408d1
|
@ -37,7 +37,7 @@
|
|||
"@babel/preset-env": "^7.0.0-beta.32",
|
||||
"@babel/preset-flow": "^7.0.0-beta.32",
|
||||
"@babel/register": "^7.0.0-beta.32",
|
||||
"flow-bin": "^0.59.0",
|
||||
"flow-bin": "^0.60.0",
|
||||
"jasmine": "^2.8.0",
|
||||
"mocha": "^4.0.1",
|
||||
"nyc": "^11.3.0"
|
||||
|
|
|
@ -20,6 +20,8 @@ import type {
|
|||
MaybeType, NumberType, StringType, BooleanType, LiteralType, ReferenceType,
|
||||
} from './types';
|
||||
|
||||
import * as t from './types';
|
||||
|
||||
import {extractPragmas} from './pragmas';
|
||||
|
||||
import {invariant} from './utils';
|
||||
|
@ -54,18 +56,16 @@ function processInterfaceDeclaration(ctx: Context, node: InterfaceDeclaration) {
|
|||
|
||||
invariant(type.id);
|
||||
|
||||
parts.push({
|
||||
kind: 'reference',
|
||||
to: type.id.slice(),
|
||||
});
|
||||
const reference = t.createReference(t.clone(type.id));
|
||||
|
||||
parts.push(reference);
|
||||
}
|
||||
|
||||
parts.push(type);
|
||||
|
||||
ctx.define(name, {
|
||||
kind: 'intersection',
|
||||
parts,
|
||||
});
|
||||
const intersection = t.createIntersection(parts);
|
||||
|
||||
ctx.define(name, intersection);
|
||||
}
|
||||
|
||||
// TODO: type params.
|
||||
|
@ -87,27 +87,22 @@ function processClassDeclaration(ctx: Context, node: ClassDeclaration) {
|
|||
|
||||
invariant(base.id);
|
||||
|
||||
const baseRef = {
|
||||
kind: 'reference',
|
||||
to: base.id.slice(),
|
||||
};
|
||||
const baseRef = t.createReference(t.clone(base.id));
|
||||
const intersection = t.createIntersection([baseRef, type]);
|
||||
|
||||
ctx.define(name, {
|
||||
kind: 'intersection',
|
||||
parts: [baseRef, type],
|
||||
});
|
||||
ctx.define(name, intersection);
|
||||
}
|
||||
|
||||
function makeType(ctx: Context, node: FlowTypeAnnotation): ?Type {
|
||||
switch (node.type) {
|
||||
case 'NullLiteralTypeAnnotation':
|
||||
return {kind: 'literal', value: null};
|
||||
return t.createLiteral(null);
|
||||
case 'BooleanTypeAnnotation':
|
||||
return {kind: 'boolean'};
|
||||
return t.createBoolean();
|
||||
case 'NumberTypeAnnotation':
|
||||
return {kind: 'number', repr: 'f64'};
|
||||
return t.createNumber('f64');
|
||||
case 'StringTypeAnnotation':
|
||||
return {kind: 'string'};
|
||||
return t.createString();
|
||||
case 'TypeAnnotation':
|
||||
return makeType(ctx, node.typeAnnotation);
|
||||
case 'NullableTypeAnnotation':
|
||||
|
@ -123,13 +118,13 @@ function makeType(ctx: Context, node: FlowTypeAnnotation): ?Type {
|
|||
case 'IntersectionTypeAnnotation':
|
||||
return makeIntersection(ctx, node);
|
||||
case 'StringLiteralTypeAnnotation':
|
||||
return {kind: 'literal', value: node.value};
|
||||
return t.createLiteral(node.value);
|
||||
case 'GenericTypeAnnotation':
|
||||
return makeReference(ctx, node);
|
||||
case 'AnyTypeAnnotation':
|
||||
return {kind: 'any'};
|
||||
return t.createAny();
|
||||
case 'MixedTypeAnnotation':
|
||||
return {kind: 'mixed'};
|
||||
return t.createMixed();
|
||||
case 'FunctionTypeAnnotation':
|
||||
return null;
|
||||
default:
|
||||
|
@ -144,10 +139,7 @@ function makeMaybe(ctx: Context, node: NullableTypeAnnotation): ?MaybeType {
|
|||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
kind: 'maybe',
|
||||
value: type,
|
||||
};
|
||||
return t.createMaybe(type);
|
||||
}
|
||||
|
||||
function makeComplexType(ctx: Context, node: ObjectTypeAnnotation): Type {
|
||||
|
@ -168,10 +160,7 @@ function makeComplexType(ctx: Context, node: ObjectTypeAnnotation): Type {
|
|||
|
||||
const parts = record.fields.length > 0 ? [record, ...maps] : maps;
|
||||
|
||||
return {
|
||||
kind: 'intersection',
|
||||
parts,
|
||||
};
|
||||
return t.createIntersection(parts);
|
||||
}
|
||||
|
||||
function makeRecord<T: ObjectTypeProperty | ClassProperty>(ctx: Context, nodes: T[]): RecordType {
|
||||
|
@ -180,10 +169,7 @@ function makeRecord<T: ObjectTypeProperty | ClassProperty>(ctx: Context, nodes:
|
|||
.filter()
|
||||
.toArray();
|
||||
|
||||
return {
|
||||
kind: 'record',
|
||||
fields,
|
||||
};
|
||||
return t.createRecord(fields);
|
||||
}
|
||||
|
||||
function makeField(ctx: Context, node: ObjectTypeProperty | ClassProperty): ?Field {
|
||||
|
@ -238,11 +224,7 @@ function makeMap(ctx: Context, node: ObjectTypeIndexer): ?MapType {
|
|||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
kind: 'map',
|
||||
keys,
|
||||
values,
|
||||
};
|
||||
return t.createMap(keys, values);
|
||||
}
|
||||
|
||||
function makeArrayType(ctx: Context, node: ArrayTypeAnnotation): ?ArrayType {
|
||||
|
@ -252,10 +234,7 @@ function makeArrayType(ctx: Context, node: ArrayTypeAnnotation): ?ArrayType {
|
|||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
kind: 'array',
|
||||
items,
|
||||
};
|
||||
return t.createArray(items);
|
||||
}
|
||||
|
||||
function makeTupleType(ctx: Context, node: TupleTypeAnnotation): ?TupleType {
|
||||
|
@ -266,10 +245,7 @@ function makeTupleType(ctx: Context, node: TupleTypeAnnotation): ?TupleType {
|
|||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
kind: 'tuple',
|
||||
items,
|
||||
};
|
||||
return t.createTuple(items);
|
||||
}
|
||||
|
||||
function makeUnionType(ctx: Context, node: UnionTypeAnnotation): ?Type {
|
||||
|
@ -286,10 +262,7 @@ function makeUnionType(ctx: Context, node: UnionTypeAnnotation): ?Type {
|
|||
return variants[0];
|
||||
}
|
||||
|
||||
return {
|
||||
kind: 'union',
|
||||
variants,
|
||||
};
|
||||
return t.createUnion(variants);
|
||||
}
|
||||
|
||||
function makeIntersection(ctx: Context, node: IntersectionTypeAnnotation): ?Type {
|
||||
|
@ -307,10 +280,7 @@ function makeIntersection(ctx: Context, node: IntersectionTypeAnnotation): ?Type
|
|||
return parts[0];
|
||||
}
|
||||
|
||||
return {
|
||||
kind: 'intersection',
|
||||
parts,
|
||||
};
|
||||
return t.createIntersection(parts);
|
||||
}
|
||||
|
||||
function makeReference(ctx: Context, node: GenericTypeAnnotation): ?Type {
|
||||
|
@ -324,10 +294,7 @@ function makeReference(ctx: Context, node: GenericTypeAnnotation): ?Type {
|
|||
return type;
|
||||
}
|
||||
|
||||
return {
|
||||
kind: 'reference',
|
||||
to: type.id.slice(),
|
||||
};
|
||||
return t.createReference(t.clone(type.id));
|
||||
}
|
||||
|
||||
export default {
|
||||
|
|
|
@ -1,35 +1,26 @@
|
|||
import wu from 'wu';
|
||||
|
||||
import {invariant, clone} from './utils';
|
||||
import {invariant} from './utils';
|
||||
import type {Type, TypeId} from './types';
|
||||
import * as t from './types';
|
||||
|
||||
function object(params: (?Type)[]): ?Type {
|
||||
invariant(params.length === 0);
|
||||
|
||||
return {
|
||||
kind: 'map',
|
||||
keys: {kind: 'mixed'},
|
||||
values: {kind: 'mixed'},
|
||||
};
|
||||
return t.createMap(t.createMixed(), t.createMixed());
|
||||
}
|
||||
|
||||
function buffer(params: (?Type)[]): ?Type {
|
||||
invariant(params.length === 0);
|
||||
|
||||
return {
|
||||
kind: 'reference',
|
||||
to: ['Buffer'],
|
||||
};
|
||||
return t.createReference(['Buffer']);
|
||||
}
|
||||
|
||||
function array(params: (?Type)[]): ?Type {
|
||||
invariant(params.length === 1);
|
||||
invariant(params[0]);
|
||||
|
||||
return {
|
||||
kind: 'array',
|
||||
items: params[0],
|
||||
};
|
||||
return t.createArray(t.clone(params[0]));
|
||||
}
|
||||
|
||||
function elemType(params: (?Type)[], resolve: TypeId => Type): ?Type {
|
||||
|
@ -51,7 +42,7 @@ function elemType(params: (?Type)[], resolve: TypeId => Type): ?Type {
|
|||
const field = wu(record.fields).find(field => field.name === key.value);
|
||||
|
||||
// TODO: what about removing "id"?
|
||||
return field ? clone(field.value) : null;
|
||||
return field ? t.clone(field.value) : null;
|
||||
}
|
||||
|
||||
function stripMaybe(params: (?Type)[], resolve: TypeId => Type): ?Type {
|
||||
|
@ -65,10 +56,10 @@ function stripMaybe(params: (?Type)[], resolve: TypeId => Type): ?Type {
|
|||
|
||||
// TODO: support for unions and nested maybe.
|
||||
if (maybe.kind !== 'maybe') {
|
||||
return clone(ref);
|
||||
return t.clone(ref);
|
||||
}
|
||||
|
||||
return clone(maybe.value);
|
||||
return t.clone(maybe.value);
|
||||
}
|
||||
|
||||
function shape(params: (?Type)[], resolve: TypeId => Type): ?Type {
|
||||
|
@ -83,14 +74,14 @@ function shape(params: (?Type)[], resolve: TypeId => Type): ?Type {
|
|||
invariant(record.kind === 'record');
|
||||
|
||||
const fields = wu(record.fields)
|
||||
.map(clone)
|
||||
.tap(field => field.required = false)
|
||||
.map(field => ({
|
||||
name: field.name,
|
||||
value: t.clone(field.value),
|
||||
required: false,
|
||||
}))
|
||||
.toArray();
|
||||
|
||||
return {
|
||||
kind: 'record',
|
||||
fields,
|
||||
};
|
||||
return t.createRecord(fields);
|
||||
}
|
||||
|
||||
function unwrap(params: (?Type)[]): ?Type {
|
||||
|
@ -98,7 +89,7 @@ function unwrap(params: (?Type)[]): ?Type {
|
|||
|
||||
const [type] = params;
|
||||
|
||||
return type ? clone(type) : null;
|
||||
return type ? t.clone(type) : null;
|
||||
}
|
||||
|
||||
function keys(params: (?Type)[], resolve: TypeId => Type): ?Type {
|
||||
|
@ -114,15 +105,12 @@ function keys(params: (?Type)[], resolve: TypeId => Type): ?Type {
|
|||
|
||||
const variants = wu(record.fields)
|
||||
.pluck('name')
|
||||
.map(name => ({kind: 'literal', value: name}))
|
||||
.map(t.createLiteral)
|
||||
.toArray();
|
||||
|
||||
// TODO: empty records.
|
||||
|
||||
return {
|
||||
kind: 'union',
|
||||
variants,
|
||||
};
|
||||
return t.createUnion(variants);
|
||||
}
|
||||
|
||||
function values(params: (?Type)[], resolve: TypeId => Type): ?Type {
|
||||
|
@ -138,16 +126,13 @@ function values(params: (?Type)[], resolve: TypeId => Type): ?Type {
|
|||
|
||||
const variants = wu(record.fields)
|
||||
.pluck('value')
|
||||
.map(clone)
|
||||
.map(t.clone)
|
||||
.toArray();
|
||||
|
||||
// TODO: empty records.
|
||||
// TODO: dedup values.
|
||||
|
||||
return {
|
||||
kind: 'union',
|
||||
variants,
|
||||
};
|
||||
return t.createUnion(variants);
|
||||
}
|
||||
|
||||
export default {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import {invariant} from './utils';
|
||||
|
||||
import type {Type} from './types';
|
||||
import * as t from './types';
|
||||
|
||||
export type Pragma =
|
||||
| TypePragma;
|
||||
|
@ -21,15 +22,10 @@ export function extractPragmas(text: string): Pragma[] {
|
|||
|
||||
invariant(['i32', 'i64', 'u32', 'u64', 'f32', 'f64'].includes(repr));
|
||||
|
||||
const pragma = {
|
||||
pragmas.push({
|
||||
kind: 'type',
|
||||
value: {
|
||||
kind: 'number',
|
||||
repr,
|
||||
},
|
||||
};
|
||||
|
||||
pragmas.push(pragma);
|
||||
value: t.createNumber(repr),
|
||||
});
|
||||
}
|
||||
|
||||
return pragmas;
|
||||
|
|
|
@ -16,7 +16,7 @@ export default function* traverse(node: Node): Generator<Node, void, boolean> {
|
|||
}
|
||||
|
||||
for (const key of keys) {
|
||||
const subNode = (node: Object)[key];
|
||||
const subNode = (node: $FlowFixMe)[key];
|
||||
|
||||
if (subNode instanceof Array) {
|
||||
for (const node of subNode) {
|
||||
|
|
70
src/types.js
70
src/types.js
|
@ -92,3 +92,73 @@ export type ReferenceType = BaseType & {
|
|||
kind: 'reference',
|
||||
to: TypeId,
|
||||
};
|
||||
|
||||
export const createRecord = (fields: *): RecordType => ({kind: 'record', fields});
|
||||
export const createArray = (items: *): ArrayType => ({kind: 'array', items});
|
||||
export const createTuple = (items: *): TupleType => ({kind: 'tuple', items});
|
||||
export const createMap = (keys: *, values: *): MapType => ({kind: 'map', keys, values});
|
||||
export const createUnion = (variants: *): UnionType => ({kind: 'union', variants});
|
||||
export const createIntersection = (parts: *): IntersectionType => ({kind: 'intersection', parts});
|
||||
export const createMaybe = (value: *): MaybeType => ({kind: 'maybe', value});
|
||||
export const createNumber = (repr: *): NumberType => ({kind: 'number', repr});
|
||||
export const createString = (): StringType => ({kind: 'string'});
|
||||
export const createBoolean = (): BooleanType => ({kind: 'boolean'});
|
||||
export const createLiteral = (value: *): LiteralType => ({kind: 'literal', value});
|
||||
export const createAny = () => ({kind: 'any'});
|
||||
export const createMixed = () => ({kind: 'mixed'});
|
||||
export const createReference = (to: *) => ({kind: 'reference', to});
|
||||
|
||||
declare function clone(Type): Type;
|
||||
declare function clone(TypeId): TypeId;
|
||||
declare function clone(?Type): ?Type;
|
||||
export function clone(type: ?Type | TypeId): ?Type | TypeId {
|
||||
if (!type) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (type instanceof Array) {
|
||||
return type.slice();
|
||||
}
|
||||
|
||||
return cloneType(type);
|
||||
}
|
||||
|
||||
function cloneType(type: Type): Type {
|
||||
switch (type.kind) {
|
||||
case 'record':
|
||||
const fields = type.fields.map(field => ({
|
||||
name: field.name,
|
||||
value: clone(field.value),
|
||||
required: field.required,
|
||||
}));
|
||||
|
||||
return createRecord(fields);
|
||||
case 'array':
|
||||
return createArray(clone(type.items));
|
||||
case 'tuple':
|
||||
return createTuple(type.items.map(clone));
|
||||
case 'map':
|
||||
return createMap(clone(type.keys), clone(type.values));
|
||||
case 'union':
|
||||
return createUnion(type.variants.map(clone));
|
||||
case 'intersection':
|
||||
return createIntersection(type.parts.map(clone));
|
||||
case 'maybe':
|
||||
return createMaybe(clone(type.value));
|
||||
case 'number':
|
||||
return createNumber(type.repr);
|
||||
case 'string':
|
||||
return createString();
|
||||
case 'boolean':
|
||||
return createBoolean();
|
||||
case 'literal':
|
||||
return createLiteral(type.value);
|
||||
case 'any':
|
||||
return createAny();
|
||||
case 'mixed':
|
||||
return createMixed();
|
||||
case 'reference':
|
||||
default:
|
||||
return createReference(type.to.slice());
|
||||
}
|
||||
}
|
||||
|
|
19
src/utils.js
19
src/utils.js
|
@ -9,22 +9,3 @@ export function last<T>(list: T[]): T {
|
|||
|
||||
return list[list.length - 1];
|
||||
}
|
||||
|
||||
export function clone<T>(that: T): T {
|
||||
if (that == null || typeof that !== 'object') {
|
||||
return that;
|
||||
}
|
||||
|
||||
if (that instanceof Array) {
|
||||
return that.map(clone);
|
||||
}
|
||||
|
||||
const obj = {};
|
||||
|
||||
for (const key in that) {
|
||||
obj[key] = clone(that[key]);
|
||||
}
|
||||
|
||||
// TODO: we skip complex objects.
|
||||
return (obj: $FlowFixMe);
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ import wu from 'wu';
|
|||
import collect from '../src';
|
||||
|
||||
function run(title) {
|
||||
let actual, expected: any;
|
||||
let actual, expected: $FlowFixMe;
|
||||
|
||||
// Run the collector only if the suite will be checked.
|
||||
before(() => {
|
||||
|
|
Loading…
Reference in New Issue