Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add missing flow utils #201

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
21 changes: 11 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,28 +15,29 @@ It's surprisingly robust and non-lossy as it stands right now, in big part thank
| ✅ | Variance | `interface A { readonly b: B, c: C }` | `interface A { +b: B, c: C }` |
| ✅ | Functions | `(a: A, b: B) => C` | `(a: A, b: B) => C` |
| ✅ | Indexers | `{[k: string]: string}` | `{[k: string]: string}` |
| | This type | `(this: X, a: A, b: B) => C` | `(a: A, b: B) => C` |
| | This type | `(this: X, a: A, b: B) => C` | `(this: X, a: A, b: B) => C` |
| | Type guards | `(a: X) => a is A` | `(a: X) => boolean` |
| ✅ | Type parameter bounds | `function f<A extends string>(a:A){}` | `function f<A: string>(a:A){}` |
| ✅ | keyof X | `keyof X` | `$Keys<X>` |
| ✅ | X[keyof X] | `X[keyof X]` | `$ElementType<X, $Keys<X>>` |
| ✅ | X[keyof X] | `X[keyof X]` | `X[$Keys<X>]` |
| ✅ | Partial | `Partial<X>` | `$Rest<X, {}>` |
| ✅ | Readonly | `Readonly<X>` | `$ReadOnly<X>` |
| ✅ | ReadonlyArray | `ReadonlyArray<X>` | `$ReadOnlyArray<X>` |
| ✅ | ReadonlySet | `ReadonlySet<X>` | `$ReadOnlySet<X>` |
| ✅ | ReadonlyMap | `ReadonlyMap<X, Y>` | `$ReadOnlyMap<X, Y>` |
| ✅ | Record | `Record<K, T>` | `{ [key: K]: T }` |
| | Pick | `Pick<T, K>` | |
| | Exclude | `Exclude<T, U>` | |
| | Extract | `Extract<T, U>` | |
| ✅ | Pick | `Pick<T, K>` | `Pick<T, K>` |
| ✅ | Exclude | `Exclude<T, U>` | `Exclude<T, U>` |
| ✅ | Extract | `Extract<T, U>` | `Extract<T, U>` |
| ✅ | Omit | `Omit<T, U>` | `Omit<T, U>` |
| ✅ | NonNullable | `NonNullable<X>` | `$NonMaybeType<X>` |
| ✅ | ReturnType | `ReturnType<F>` | `$Call<<R>((...args: any[]) => R) => R, F>` |
| ✅ | ReturnType | `ReturnType<F>` | `ReturnType<F>` |
| | InstanceType | `InstanceType<X>` | |
| | Required | `Required<X>` | |
| | Required | `Required<X>` | `Required<X>` |
| | ThisType | `ThisType<X>` | |
| ✅ | T['string'] | `T['string']` | `$PropertyType<T, k>` |
| ✅ | T[k] | `T[k]` | `$ElementType<T, k>` |
| ✅ | Mapped types | `{[K in keyof Obj]: Obj[K]}` | `$ObjMapi<Obj, <K>(K) => $ElementType<Obj, K>>` |
| ✅ | T['string'] | `T['string']` | `T['string']` |
| ✅ | T[k] | `T[k]` | `T[k]` |
| ✅ | Mapped types | `{[K in keyof Obj]: Obj[K]}` | `$ObjMapi<Obj, <K>(K) => Obj[K]>` |
| | Conditional types | `A extends B ? C : D` | `any` |
| ✅ | typeof operator | `typeof foo` | `typeof foo` |
| ✅ | Tuple type | `[number, string]` | `[number, string]` |
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
"eslint-plugin-import": "^2.25.4",
"eslint-plugin-jest": "^25.3.4",
"eslint-plugin-prettier": "^4.0.0",
"flow-bin": "^0.195.0",
"flow-bin": "^0.217.2",
"jest": "^27.4.7",
"mongodb": "^4.1.3"
},
Expand Down
21 changes: 9 additions & 12 deletions src/__tests__/__snapshots__/duplicated-names.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,9 @@ declare export var AuthMechanism: {
+MONGODB_CR: \\"MONGODB-CR\\",
...
};
export type AuthMechanismType1 = $ElementType<
typeof AuthMechanism,
$Keys<typeof AuthMechanism>
>;
export type AuthMechanismType1 = (typeof AuthMechanism)[$Keys<
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't know what is adding parenthesis around (typeof AuthMechanism) but it's not breaking types 🤷‍♂️

typeof AuthMechanism
>];
"
`;

Expand All @@ -26,10 +25,9 @@ exports[`should handle variable & type having same name 1`] = `
+MONGODB_CR: \\"MONGODB-CR\\",
...
};
export type AuthMechanismType = $ElementType<
typeof AuthMechanism,
$Keys<typeof AuthMechanism>
>;
export type AuthMechanismType = (typeof AuthMechanism)[$Keys<
typeof AuthMechanism
>];
"
`;

Expand All @@ -38,10 +36,9 @@ exports[`should support generic type rename 1`] = `
+off: \\"off\\",
...
}>;
export type ProfilingLevelType = $ElementType<
typeof ProfilingLevel,
$Keys<typeof ProfilingLevel>
>;
export type ProfilingLevelType = (typeof ProfilingLevel)[$Keys<
typeof ProfilingLevel
>];
export type Callback<T = any> = (error?: Error, result?: T) => void;
declare export var callback: Callback<ProfilingLevelType>;
"
Expand Down
19 changes: 13 additions & 6 deletions src/__tests__/__snapshots__/function-exports.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -46,17 +46,24 @@ declare export function toString(b: string): void;
"
`;

exports[`should not break with Promise return types - issue 156 1`] = `
"declare export var fn: () => Promise<void>;
exports[`should keep default parameters in functions 1`] = `
"declare function addClickListener<T = Error>(onclick: (e: Event) => void): T;
"
`;

exports[`should remove default parameters from functions 1`] = `
"declare function addClickListener<T>(onclick: (e: Event) => void): T;
exports[`should keep this annotation in functions 1`] = `
"declare function addClickListener(
onclick: (this: void, e: Event) => void
): void;
"
`;

exports[`should not break with Promise return types - issue 156 1`] = `
"declare export var fn: () => Promise<void>;
"
`;

exports[`should remove this annotation from functions 1`] = `
"declare function addClickListener(onclick: (e: Event) => void): void;
exports[`should transform generic \`<T extends E>\` to \`<T: E>\` on functions 1`] = `
"declare function addClickListener<T: Error>(onclick: (e: Event) => void): T;
"
`;
12 changes: 6 additions & 6 deletions src/__tests__/__snapshots__/interfaces.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -139,23 +139,23 @@ exports[`should handle untyped object binding pattern 1`] = `
"
`;

exports[`should remove generic defaults in call signature 1`] = `
exports[`should keep generic defaults in call signature 1`] = `
"declare interface AbstractLevelDOWN<K, V> {}
declare interface AbstractLevelDOWNConstructor {
<K, V>(location: string): AbstractLevelDOWN<K, V>;
<K = any, V = any>(location: string): AbstractLevelDOWN<K, V>;
}
"
`;

exports[`should remove this in call signature 1`] = `
exports[`should keep this in call signature 1`] = `
"declare interface Arc<This, Datum> {
(d: Datum, ...args: any[]): string | null;
(this: This, d: Datum, ...args: any[]): string | null;
}
declare interface D<This, Datum> {
new(d: Datum, ...args: any[]): void;
new(this: D<This, Datum>, d: Datum, ...args: any[]): void;
}
declare interface C<This, Datum> {
(d: Datum, ...args: any[]): any;
(this: This, d: Datum, ...args: any[]): any;
}
"
`;
Expand Down
7 changes: 2 additions & 5 deletions src/__tests__/__snapshots__/mapped-types.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,7 @@ declare type MappedUnion = $ObjMapi<
{ [k: SourceUnion]: any },
<K>(K) => Ref<number>
>;
declare type MappedObj = $ObjMapi<
SourceObject,
<K>(K) => Ref<$ElementType<SourceObject, K>>
>;
declare type ConstantKey = $PropertyType<MappedObj, \\"a\\">;
declare type MappedObj = $ObjMapi<SourceObject, <K>(K) => Ref<SourceObject[K]>>;
declare type ConstantKey = MappedObj[\\"a\\"];
"
`;
6 changes: 3 additions & 3 deletions src/__tests__/__snapshots__/spread.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ declare var combination: Foo & Bar;
"
`;

exports[`should use spread when performing union of object types 1`] = `
exports[`should not use spread when performing union of inexact object types 1`] = `
"declare type Foo = {
foo: number,
...
Expand All @@ -16,11 +16,11 @@ declare type Bar = {
bar: string,
...
};
declare var combination: { ...Foo, ...Bar };
declare var combination: Foo & Bar;
"
`;

exports[`should use spread when performing union of object types 2`] = `
exports[`should use spread when performing union of exact object types 1`] = `
"declare type Foo = {|
foo: number,
|};
Expand Down
31 changes: 16 additions & 15 deletions src/__tests__/__snapshots__/utility-types.spec.ts.snap
Original file line number Diff line number Diff line change
@@ -1,29 +1,29 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should handle Omit type 1`] = `
"declare type A = $Diff<
"declare type A = Omit<
{
a: string,
b: number,
...
},
{ a: any }
\\"a\\"
>;
declare type B = $Diff<
declare type B = Omit<
{
a: string,
b: number,
...
},
{ a: any, b: any }
\\"a\\" | \\"b\\"
>;
declare type O = {
a: string,
b: number,
...
};
declare type U = \\"a\\";
declare type C = $Diff<O, { [key: U]: any }>;
declare type C = Omit<O, U>;
"
`;

Expand All @@ -32,30 +32,31 @@ exports[`should handle utility types 1`] = `
a: number,
...
}>;
declare type B = $Rest<
{
a: number,
...
},
{ ... }
>;
declare type B = Partial<{
a: number,
...
}>;
declare type C = $NonMaybeType<string | null>;
declare type D = $ReadOnlyArray<string>;
declare type E = $Call<<R>((...args: any[]) => R) => R, () => string>;
declare type E = ReturnType<() => string>;
declare type F = { [key: string]: number, ... };
declare type G = $ReadOnlySet<number>;
declare type H = $ReadOnlyMap<string, number>;
declare type I = Pick<A, \\"foo\\" | \\"bar\\">;
declare type J = Exclude<1 | 2 | 3 | 4, 1 | 3>;
declare type K = Extract<A | B>;
declare type L = Required<A>;
declare type A1<Readonly> = Readonly;
declare type B1<Partial> = Partial;
declare type C1<NonNullable> = NonNullable;
declare type D1<ReadonlyArray> = ReadonlyArray;
declare type E1<ReturnType> = ReturnType;
declare type F1<Record> = Record;
declare type A2<T> = $ReadOnly<T>;
declare type B2<T> = $Rest<T, { ... }>;
declare type B2<T> = Partial<T>;
declare type C2<T> = $NonMaybeType<T>;
declare type D2<T> = $ReadOnlyArray<T>;
declare type E2<T> = $Call<<R>((...args: any[]) => R) => R, () => T>;
declare type E2<T> = ReturnType<() => T>;
declare type F2<T, U> = { [key: T]: U, ... };
"
`;
12 changes: 10 additions & 2 deletions src/__tests__/function-exports.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,22 +51,30 @@ export function routerReducer(state?: RouterState): RouterState;
expect(result).toBeValidFlowTypeDeclarations();
});

it("should remove this annotation from functions", () => {
it("should keep this annotation in functions", () => {
const ts =
"function addClickListener(onclick: (this: void, e: Event) => void): void;";
const result = compiler.compileDefinitionString(ts, { quiet: true });
expect(beautify(result)).toMatchSnapshot();
expect(result).toBeValidFlowTypeDeclarations();
});

it("should remove default parameters from functions", () => {
it("should keep default parameters in functions", () => {
const ts =
"function addClickListener<T = Error>(onclick: (e: Event) => void): T;";
const result = compiler.compileDefinitionString(ts, { quiet: true });
expect(beautify(result)).toMatchSnapshot();
expect(result).toBeValidFlowTypeDeclarations();
});

it("should transform generic `<T extends E>` to `<T: E>` on functions", () => {
const ts =
"function addClickListener<T extends Error>(onclick: (e: Event) => void): T;";
const result = compiler.compileDefinitionString(ts, { quiet: true });
expect(beautify(result)).toMatchSnapshot();
expect(result).toBeValidFlowTypeDeclarations();
});

it("should not break with Promise return types - issue 156", () => {
const ts = "export declare const fn: () => Promise<void>;";
const result = compiler.compileDefinitionString(ts, { quiet: true });
Expand Down
6 changes: 3 additions & 3 deletions src/__tests__/interfaces.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,14 +105,14 @@ it("should support call signature", () => {
expect(result).toBeValidFlowTypeDeclarations();
});

it("should remove this in call signature", () => {
it("should keep this in call signature", () => {
const ts = `
interface Arc<This, Datum> {
(this: This, d: Datum, ...args: any[]): string | null;
}

interface D<This, Datum> {
new (this: This, d: Datum, ...args: any[]);
new (this: D<This, Datum>, d: Datum, ...args: any[]);
}

interface C<This, Datum> {
Expand All @@ -124,7 +124,7 @@ interface C<This, Datum> {
expect(result).toBeValidFlowTypeDeclarations();
});

it("should remove generic defaults in call signature", () => {
it("should keep generic defaults in call signature", () => {
const ts = `
interface AbstractLevelDOWN<K, V> {}
interface AbstractLevelDOWNConstructor {
Expand Down
35 changes: 20 additions & 15 deletions src/__tests__/spread.spec.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,32 @@
import { compiler, beautify } from "..";
import "../test-matchers";

it("should use spread when performing union of object types", () => {
it("should use spread when performing union of exact object types", () => {
const ts = `
type Foo = { foo: number };
type Bar = { bar: string };
const combination: Foo & Bar;
`;
const result = compiler.compileDefinitionString(ts, {
quiet: true,
inexact: false,
});
expect(beautify(result)).toMatchSnapshot();
expect(result).toBeValidFlowTypeDeclarations();
});

{
const result = compiler.compileDefinitionString(ts, { quiet: true });
expect(beautify(result)).toMatchSnapshot();
expect(result).toBeValidFlowTypeDeclarations();
}

{
const result = compiler.compileDefinitionString(ts, {
quiet: true,
inexact: false,
});
expect(beautify(result)).toMatchSnapshot();
expect(result).toBeValidFlowTypeDeclarations();
}
it("should not use spread when performing union of inexact object types", () => {
const ts = `
type Foo = { foo: number };
type Bar = { bar: string };
const combination: Foo & Bar;
`;
const result = compiler.compileDefinitionString(ts, {
quiet: true,
inexact: true,
});
expect(beautify(result)).toMatchSnapshot();
expect(result).toBeValidFlowTypeDeclarations();
});

it("should not insert spread when performing union of class types", () => {
Expand Down
4 changes: 4 additions & 0 deletions src/__tests__/utility-types.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ type E = ReturnType<() => string>
type F = Record<string, number>
type G = ReadonlySet<number>
type H = ReadonlyMap<string, number>
type I = Pick<A, 'foo' | 'bar'>
type J = Exclude<1 | 2 | 3 | 4, 1 | 3>
type K = Extract<A | B>
type L = Required<A>

type A1<Readonly> = Readonly
type B1<Partial> = Partial
Expand Down
8 changes: 2 additions & 6 deletions src/printers/function.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,8 @@ export const functionType = (
| ts.ConstructSignatureDeclaration,
dotAsReturn = false,
): string => {
const params = func.parameters
.filter(
param => !(ts.isIdentifier(param.name) && param.name.text === "this"),
)
.map(printers.common.parameter);
const generics = printers.common.genericsWithoutDefault(func.typeParameters);
const params = func.parameters.map(printers.common.parameter);
const generics = printers.common.generics(func.typeParameters);
const returns = func.type ? printers.node.printType(func.type) : "void";

const firstPass = `${generics}(${params.join(", ")})${
Expand Down