From 095cd800a0ff3ca4fe697b2800f1cb5fab175d93 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Sun, 21 Oct 2018 20:31:11 +0200 Subject: [PATCH] Implement principle 1: an imported type is also exported to other modules. Principle 1 is from: https://github.com/cristianoc/genType/issues/70. This simplifies a bunch of examples. In particular, those discusse in https://github.com/cristianoc/genType/issues/63. --- .../src/basics/Records.bs.js | 5 ++ .../src/basics/Records.re | 5 +- .../src/basics/Records.re.js | 4 ++ .../reason-react-example/src/basics/Types.re | 9 +-- .../src/basics/Types.re.js | 6 +- .../typescript-react-example/src/MyMath.ts | 3 + .../src/UseWrapJsValue.bs.js | 5 ++ .../src/UseWrapJsValue.re | 7 ++- .../src/UseWrapJsValue.tsx | 8 ++- .../src/WrapJsValue.re | 8 +-- .../src/WrapJsValue.tsx | 9 ++- src/CodeItem.re | 2 +- src/EmitJs.re | 21 ++++--- src/EmitTyp.re | 46 +++++++++++---- src/EmitTyp.rei | 7 ++- src/Translation.re | 59 ++++++++++--------- 16 files changed, 135 insertions(+), 69 deletions(-) diff --git a/examples/reason-react-example/src/basics/Records.bs.js b/examples/reason-react-example/src/basics/Records.bs.js index 750cbe8c0..e7aa88a29 100644 --- a/examples/reason-react-example/src/basics/Records.bs.js +++ b/examples/reason-react-example/src/basics/Records.bs.js @@ -53,6 +53,10 @@ function computeNestedNestedHalfNullable() { return 0; } +function useTypeImportedInOtherModule(x) { + return x; +} + var origin = /* record */[ /* x */0, /* y */0, @@ -69,4 +73,5 @@ exports.computeNested = computeNested; exports.computeNestedNested = computeNestedNested; exports.computeNestedNestedNullable = computeNestedNestedNullable; exports.computeNestedNestedHalfNullable = computeNestedNestedHalfNullable; +exports.useTypeImportedInOtherModule = useTypeImportedInOtherModule; /* No side effect */ diff --git a/examples/reason-react-example/src/basics/Records.re b/examples/reason-react-example/src/basics/Records.re index 041537693..3b0a8defe 100644 --- a/examples/reason-react-example/src/basics/Records.re +++ b/examples/reason-react-example/src/basics/Records.re @@ -145,4 +145,7 @@ let computeNestedNestedHalfNullable = (_: bigType): int => 0; type testMutable = { mutable mutableField: int, immutableField: int, -}; \ No newline at end of file +}; + +[@genType] +let useTypeImportedInOtherModule = (x: Types.weekday) => x; \ No newline at end of file diff --git a/examples/reason-react-example/src/basics/Records.re.js b/examples/reason-react-example/src/basics/Records.re.js index f95f75ee3..830c1dc94 100644 --- a/examples/reason-react-example/src/basics/Records.re.js +++ b/examples/reason-react-example/src/basics/Records.re.js @@ -7,6 +7,8 @@ // $FlowExpectedError: Reason checked type sufficiently const RecordsBS = require('./Records.bs'); +import type {weekday as Types_weekday} from './Types.re'; + export type coord = {| +x: number, +y: number, @@ -94,3 +96,5 @@ export type bigType = {| export const computeNestedNestedHalfNullable: (bigType) => number = RecordsBS.computeNestedNestedHalfNullable; export type testMutable = {|mutableField: number, +immutableField: number|}; + +export const useTypeImportedInOtherModule: (Types_weekday) => Types_weekday = RecordsBS.useTypeImportedInOtherModule; diff --git a/examples/reason-react-example/src/basics/Types.re b/examples/reason-react-example/src/basics/Types.re index b666a6137..ef6fce36b 100644 --- a/examples/reason-react-example/src/basics/Types.re +++ b/examples/reason-react-example/src/basics/Types.re @@ -56,12 +56,9 @@ let identity = (x: anInterestingFlowType) => x; [@genType.import "./SomeFlowTypes"] type weekday; -[@genType] -type t = weekday; - -[@bs.module "./SomeFlowTypes"] external saturday: t = "SATURDAY"; -[@bs.module "./SomeFlowTypes"] external sunday: t = "SUNDAY"; -[@bs.module "./SomeFlowTypes"] external monday: t = "MONDAY"; +[@bs.module "./SomeFlowTypes"] external saturday: weekday = "SATURDAY"; +[@bs.module "./SomeFlowTypes"] external sunday: weekday = "SUNDAY"; +[@bs.module "./SomeFlowTypes"] external monday: weekday = "MONDAY"; [@genType] let isWeekend = day => day === saturday || day === sunday; diff --git a/examples/reason-react-example/src/basics/Types.re.js b/examples/reason-react-example/src/basics/Types.re.js index 5d0ddb452..25c618dc8 100644 --- a/examples/reason-react-example/src/basics/Types.re.js +++ b/examples/reason-react-example/src/basics/Types.re.js @@ -54,11 +54,13 @@ export type peopleArray = Array<{|+name: string, +nickname: ?string|}>; export opaque type myObj = Obj_t; +export type { anInterestingFlowType }; + export const identity: (anInterestingFlowType) => anInterestingFlowType = TypesBS.identity; -export opaque type t = weekday; +export type { weekday }; -export const isWeekend: (t) => boolean = TypesBS.isWeekend; +export const isWeekend: (weekday) => boolean = TypesBS.isWeekend; export const testFunctionOnOptionsAsArgument: (?a, ((?a) => T1)) => T1 = function _(Arg1, Arg2) { const result = TypesBS.testFunctionOnOptionsAsArgument((Arg1 == null ? undefined : Arg1), Arg2); return result }; diff --git a/examples/typescript-react-example/src/MyMath.ts b/examples/typescript-react-example/src/MyMath.ts index b37763721..beb91acc2 100644 --- a/examples/typescript-react-example/src/MyMath.ts +++ b/examples/typescript-react-example/src/MyMath.ts @@ -1,3 +1,4 @@ + /* @flow strict */ export const round: ((_: number) => number) = Math.round; @@ -16,3 +17,5 @@ export class AbsoluteValue { return this.prop < 0 ? -this.prop : this.prop; } } + +export type stringFunction = (_: string) => string; diff --git a/examples/typescript-react-example/src/UseWrapJsValue.bs.js b/examples/typescript-react-example/src/UseWrapJsValue.bs.js index 5a283649c..9981feb1e 100644 --- a/examples/typescript-react-example/src/UseWrapJsValue.bs.js +++ b/examples/typescript-react-example/src/UseWrapJsValue.bs.js @@ -6,5 +6,10 @@ function useGetProp(x) { return x.getProp() + 1 | 0; } +function useTypeImportedInOtherModule(x) { + return x; +} + exports.useGetProp = useGetProp; +exports.useTypeImportedInOtherModule = useTypeImportedInOtherModule; /* No side effect */ diff --git a/examples/typescript-react-example/src/UseWrapJsValue.re b/examples/typescript-react-example/src/UseWrapJsValue.re index 7ebdb6522..fa6bebce3 100644 --- a/examples/typescript-react-example/src/UseWrapJsValue.re +++ b/examples/typescript-react-example/src/UseWrapJsValue.re @@ -1,3 +1,6 @@ [@genType] -let useGetProp = (x: WrapJsValue.AbsoluteValue.imported) => - x->WrapJsValue.AbsoluteValue.getProp + 1; \ No newline at end of file +let useGetProp = (x: WrapJsValue.AbsoluteValue.t) => + x->WrapJsValue.AbsoluteValue.getProp + 1; + +[@genType] +let useTypeImportedInOtherModule = (x: WrapJsValue.stringFunction) => x; \ No newline at end of file diff --git a/examples/typescript-react-example/src/UseWrapJsValue.tsx b/examples/typescript-react-example/src/UseWrapJsValue.tsx index f8e49f932..95ffffe0d 100644 --- a/examples/typescript-react-example/src/UseWrapJsValue.tsx +++ b/examples/typescript-react-example/src/UseWrapJsValue.tsx @@ -3,6 +3,10 @@ // tslint:disable-next-line:no-var-requires const UseWrapJsValueBS = require('./UseWrapJsValue.bs'); -import {AbsoluteValue_imported as WrapJsValue_AbsoluteValue_imported} from './WrapJsValue'; +import {AbsoluteValue_t as WrapJsValue_AbsoluteValue_t} from './WrapJsValue'; -export const useGetProp: (_1:WrapJsValue_AbsoluteValue_imported) => number = UseWrapJsValueBS.useGetProp; +import {stringFunction as WrapJsValue_stringFunction} from './WrapJsValue'; + +export const useGetProp: (_1:WrapJsValue_AbsoluteValue_t) => number = UseWrapJsValueBS.useGetProp; + +export const useTypeImportedInOtherModule: (_1:WrapJsValue_stringFunction) => WrapJsValue_stringFunction = UseWrapJsValueBS.useTypeImportedInOtherModule; diff --git a/examples/typescript-react-example/src/WrapJsValue.re b/examples/typescript-react-example/src/WrapJsValue.re index 5d60fbc59..f867e4bbb 100644 --- a/examples/typescript-react-example/src/WrapJsValue.re +++ b/examples/typescript-react-example/src/WrapJsValue.re @@ -28,9 +28,6 @@ module AbsoluteValue = { [@genType.as "AbsoluteValue"] type t = {. "getAbs": (. unit) => int}; - [@genType] - type imported = t; - /* This is untyped */ [@bs.send] external getProp: t => int = "getProp"; @@ -45,4 +42,7 @@ module AbsoluteValue = { let useGetProp = (x: AbsoluteValue.t) => x->AbsoluteValue.getProp + 1; [@genType] -let useGetAbs = (x: AbsoluteValue.t) => x->AbsoluteValue.getAbs + 1; \ No newline at end of file +let useGetAbs = (x: AbsoluteValue.t) => x->AbsoluteValue.getAbs + 1; + +[@genType.import "./MyMath"] +type stringFunction; \ No newline at end of file diff --git a/examples/typescript-react-example/src/WrapJsValue.tsx b/examples/typescript-react-example/src/WrapJsValue.tsx index 1f58910b4..fa76cd6c0 100644 --- a/examples/typescript-react-example/src/WrapJsValue.tsx +++ b/examples/typescript-react-example/src/WrapJsValue.tsx @@ -21,6 +21,8 @@ const WrapJsValueBS = require('./WrapJsValue.bs'); import {AbsoluteValue as AbsoluteValue_t} from './MyMath'; +import {stringFunction} from './MyMath'; + // tslint:disable-next-line:interface-over-type-literal export type point = {readonly x: number, readonly y?: number}; @@ -28,9 +30,12 @@ export const roundedNumber: number = WrapJsValueBS.roundedNumber; export const areaValue: number = WrapJsValueBS.areaValue; -// tslint:disable-next-line:max-classes-per-file -export abstract class AbsoluteValue_imported { protected opaque!: any }; /* simulate opaque types */ +// tslint:disable-next-line:interface-over-type-literal +export type AbsoluteValue_t = AbsoluteValue_t; export const useGetProp: (_1:AbsoluteValue_t) => number = WrapJsValueBS.useGetProp; export const useGetAbs: (_1:AbsoluteValue_t) => number = WrapJsValueBS.useGetAbs; + +// tslint:disable-next-line:interface-over-type-literal +export type stringFunction = stringFunction; diff --git a/src/CodeItem.re b/src/CodeItem.re index cb84bf2b3..2eb32ec1e 100644 --- a/src/CodeItem.re +++ b/src/CodeItem.re @@ -4,7 +4,7 @@ type exportType = { opaque: bool, typeVars: list(string), resolvedTypeName: string, - typ, + optTyp: option(typ), }; type exportVariantType = { diff --git a/src/EmitJs.re b/src/EmitJs.re index 68e8ee4ba..c1f89f8fa 100644 --- a/src/EmitJs.re +++ b/src/EmitJs.re @@ -29,17 +29,24 @@ let requireModule = (~early, ~env, ~importPath, ~strict=false, moduleName) => { let createExportTypeMap = (~language, codeItems): typeMap => { let updateExportTypeMap = (exportTypeMap: typeMap, codeItem): typeMap => { let addExportType = - ({resolvedTypeName, typeVars, typ, _}: CodeItem.exportType) => { + ({resolvedTypeName, typeVars, optTyp, _}: CodeItem.exportType) => { if (Debug.codeItems) { logItem( - "Export Type: %s%s = %s\n", + "Export Type: %s%s%s\n", resolvedTypeName, typeVars == [] ? "" : "(" ++ (typeVars |> String.concat(",")) ++ ")", - typ |> EmitTyp.typToString(~language), + switch (optTyp) { + | Some(typ) => " = %s" ++ (typ |> EmitTyp.typToString(~language)) + | None => "" + }, ); }; - exportTypeMap |> StringMap.add(resolvedTypeName, (typeVars, typ)); + switch (optTyp) { + | Some(typ) => + exportTypeMap |> StringMap.add(resolvedTypeName, (typeVars, typ)) + | None => exportTypeMap + }; }; switch (codeItem) { | CodeItem.ExportType(exportType) => exportType |> addExportType @@ -113,15 +120,15 @@ let emitExportType = ~early=false, ~emitters, ~language, - {CodeItem.opaque, typeVars, resolvedTypeName, typ}, + {CodeItem.opaque, typeVars, resolvedTypeName, optTyp}, ) => - typ + resolvedTypeName |> EmitTyp.emitExportType( ~early, ~emitters, ~language, ~opaque, - ~resolvedTypeName, + ~optTyp, ~typeVars, ); diff --git a/src/EmitTyp.re b/src/EmitTyp.re index ec16203b0..5ddc67f44 100644 --- a/src/EmitTyp.re +++ b/src/EmitTyp.re @@ -213,22 +213,39 @@ let emitExportDefault = (~emitters, ~config, name) => }; let emitExportType = - (~early, ~emitters, ~language, ~opaque, ~resolvedTypeName, ~typeVars, typ) => { + ( + ~early, + ~emitters, + ~language, + ~opaque, + ~optTyp, + ~typeVars, + resolvedTypeName, + ) => { let export = early ? Emitters.exportEarly : Emitters.export; let typeParamsString = genericsString(~typeVars); switch (language) { | Flow => - "export" - ++ (opaque ? " opaque " : " ") - ++ "type " - ++ resolvedTypeName - ++ typeParamsString - ++ " = " - ++ (typ |> typToString(~language)) - ++ ";" - |> export(~emitters) - + switch (optTyp) { + | Some(typ) => + "export" + ++ (opaque ? " opaque " : " ") + ++ "type " + ++ resolvedTypeName + ++ typeParamsString + ++ " = " + ++ (typ |> typToString(~language)) + ++ ";" + |> export(~emitters) + | None => + "export" + ++ (opaque ? " opaque " : " ") + ++ "type " + ++ (resolvedTypeName |> EmitText.brackets) + ++ ";" + |> export(~emitters) + } | Typescript => if (opaque) { /* Represent an opaque type as an absract class with a field called 'opaque'. @@ -250,7 +267,12 @@ let emitExportType = ++ resolvedTypeName ++ typeParamsString ++ " = " - ++ (typ |> typToString(~language)) + ++ ( + switch (optTyp) { + | Some(typ) => typ |> typToString(~language) + | None => resolvedTypeName + } + ) ++ ";" |> export(~emitters); } diff --git a/src/EmitTyp.rei b/src/EmitTyp.rei index 3ed1af226..c9d0eef01 100644 --- a/src/EmitTyp.rei +++ b/src/EmitTyp.rei @@ -3,7 +3,8 @@ open GenTypeCommon; let blockTagValue: (~language: language, int) => string; let componentExportName: - (~language: language, ~fileName: ModuleName.t, ~moduleName: ModuleName.t) => string; + (~language: language, ~fileName: ModuleName.t, ~moduleName: ModuleName.t) => + string; let emitExportConst: ( @@ -57,9 +58,9 @@ let emitExportType: ~emitters: Emitters.t, ~language: language, ~opaque: bool, - ~resolvedTypeName: string, + ~optTyp: option(typ), ~typeVars: list(string), - typ + string ) => Emitters.t; diff --git a/src/Translation.re b/src/Translation.re index 58d6aa316..7c386e815 100644 --- a/src/Translation.re +++ b/src/Translation.re @@ -17,9 +17,9 @@ let combine = (translations: list(t)): t => ); let translateExportType = - (~opaque, ~typeVars, ~typeName, ~typeEnv, typ): CodeItem.t => { + (~opaque, ~typeVars, ~optTyp, ~typeEnv, typeName): CodeItem.t => { let resolvedTypeName = typeName |> TypeEnv.addModulePath(~typeEnv); - ExportType({opaque, typeVars, resolvedTypeName, typ}); + ExportType({opaque, typeVars, resolvedTypeName, optTyp}); }; let variantLeafTypeName = (typeName, leafName) => @@ -61,7 +61,7 @@ let translateConstructorDeclaration = opaque: true, typeVars, resolvedTypeName: variantTypeNameResolved, - typ: mixedOrUnknown(~language), + optTyp: Some(mixedOrUnknown(~language)), }, constructorTyp, argTypes, @@ -102,12 +102,7 @@ let translateValue = (~language, ~fileName, ~typeEnv, ~typeExpr, name): t => { typeEnv |> TypeEnv.getValueAccessPath(~name=resolvedName); let codeItems = [ - CodeItem.ExportValue({ - fileName, - resolvedName, - valueAccessPath, - typ, - }), + CodeItem.ExportValue({fileName, resolvedName, valueAccessPath, typ}), ]; {dependencies: typeExprTranslation.dependencies, codeItems}; }; @@ -212,7 +207,7 @@ let translateComponent = (~language, ~fileName, ~typeEnv, ~typeExpr, name): t => opaque: false, typeVars, resolvedTypeName: propsTypeName, - typ: propsType, + optTyp: Some(propsType), }, fileName, moduleName, @@ -371,7 +366,7 @@ let translatePrimitive = opaque: false, typeVars, resolvedTypeName: propsTypeName, - typ: propsTyp, + optTyp: Some(propsTyp), }, importAnnotation: importString |> Annotation.importAnnotationFromString, @@ -450,19 +445,14 @@ let translateTypeDeclaration = }; {name, optional, mutable_, typ: typ1}; }); - let typ = Record(fields); + let optTyp = Some(Record(fields)); let typeVars = TypeVars.extract(typeParams); let typeName = Ident.name(dec.typ_id); { dependencies, codeItems: [ - translateExportType( - ~opaque=false, - ~typeVars, - ~typeName, - ~typeEnv, - typ, - ), + typeName + |> translateExportType(~opaque=false, ~typeVars, ~optTyp, ~typeEnv), ], }; @@ -479,13 +469,13 @@ let translateTypeDeclaration = | None => { dependencies: [], codeItems: [ - translateExportType( - ~opaque=true, - ~typeVars, - ~typeName, - ~typeEnv, - mixedOrUnknown(~language), - ), + typeName + |> translateExportType( + ~opaque=true, + ~typeVars, + ~optTyp=Some(mixedOrUnknown(~language)), + ~typeEnv, + ), ], } | Some(coreType) => @@ -535,7 +525,13 @@ let translateTypeDeclaration = | _ => typeExprTranslation.typ }; let codeItems = [ - translateExportType(~opaque, ~typeVars, ~typeName, ~typeEnv, typ), + typeName + |> translateExportType( + ~opaque, + ~typeVars, + ~optTyp=Some(typ), + ~typeEnv, + ), ]; {dependencies: typeExprTranslation.dependencies, codeItems}; }; @@ -602,6 +598,15 @@ let translateTypeDeclaration = cmtFile: None, }), ), + /* Make the imported type usable from other modules by exporting it too. */ + dec.typ_id + |> Ident.name + |> translateExportType( + ~opaque=false, + ~typeVars=[], + ~optTyp=None, + ~typeEnv, + ), ]; {dependencies: [], codeItems}; | _ => {dependencies: [], codeItems: []}