diff --git a/types/meteor-dburles-collection-helpers/collectionHelpers.d.ts b/types/meteor-dburles-collection-helpers/collectionHelpers.d.ts new file mode 100644 index 0000000000..1849e41781 --- /dev/null +++ b/types/meteor-dburles-collection-helpers/collectionHelpers.d.ts @@ -0,0 +1,198 @@ +// tslint:disable-next-line no-single-declare-module +declare module 'meteor/dburles:collection-helpers' { + export {}; + type PropertyNamesMatching = { + [K in keyof T]: T[K] extends TPred ? K : never; + }[keyof T]; + type PropertyNamesNotMatching = { + [K in keyof T]: T[K] extends TPred ? never : K; + }[keyof T]; + + /** + * Use to declare a non-function helper - it'll be optional when inserting values but required when adding helpers + * Tip: Use OptionalHelper to declare a helper you might or might not provide - it won't be required + * when providing helpers, and won't be guaranteed on Full + */ + // "T extends T ? ... : never" looks tautological, but actually serves to distribute over union types + // https://github.com/microsoft/TypeScript/issues/28791#issuecomment-443520161 + export type Helper = T extends T + ? T extends FlavorUnsupportedTypes + ? T | HelperBrand + : T & HelperFlavor + : never; + + // where possible, helpers are tagged as (T & HelperFlavor) + // this is a technique called "flavoring" - (T & HelperFlavor) is assignable both to and from T for most T, + // but the presence of the flavor can be checked since (T & HelperFlavor) is also assignable to HelperFlavor + // (note that HelperFlavor is a "weak type" - since all its properties are optional, you might think anything + // would be assignable to it, but Typescript prohibits assigning any type that doesn't share at least one + // property with it) + // weirdly, ({} & HelperFlavor) still accepts {}! + interface HelperFlavor { + _meteor_dburles_collection_helpers_isHelper?: Flavor; + } + // for types where (T & HelperFlavor) === never, (T | HelperBrand) is used instead, + // and the HelperBrand is stripped off in HelpersOf + // this is less preferable, because it means: + // - on a value of the original interface type TInterface, Helper and Helper properties will + // not be assignable to null or to (T | null) respectively + // - Helpers> !== Helpers when T has Helper or Helper properties + // however, this appears to be a limitation of Typescript (with strict null checks on, null and undefined + // simply *can't* extend anything besides themselves (and void in undefined's case), so there's no way to + // flavor them) + interface HelperBrand { + _meteor_dburles_collection_helpers_isBrandUnsupportedHelper: Brand; + } + // types where HelperBrand will be used instead of HelperFlavor + type FlavorUnsupportedTypes = null | undefined; + + /** + * All methods and Helpers declared on the type, made non-optional. + * This is what's required when defining helpers for a collection. + */ + export type Helpers = FlavorAsHelpers< + T, + // methods will only ever get called on a Full (unless you directly declare a Helpers, but *why*) + ThisType> & (T extends T ? HelpersOf>> : never) + >; + + // used to flavor Helpers so we can get back to the original T if needed + // not to be confused with HelperFlavor + interface HelpersFlavor { + _meteor_dburles_collection_helpers_isHelpersOf?: [Flavor, T]; + } + // apply HelpersFlavor, but only if the resulting type wouldn't be never or weak + type FlavorAsHelpers = [TToFlavor] extends [TToFlavor & HelpersFlavor] + ? TToFlavor & HelpersFlavor + : TToFlavor; + /** + * NonHelpers> === T + */ + export type NonHelpers = T extends HelpersFlavor ? U : T; + + // To get all the helper properties, we union the list of function properties and the list of Helper properties. + // Make anything not marked optional required, and anything marked optional optional. + type HelpersOf = T extends T + ? RemoveHelperBrands< + Required< + Pick< + T, + Exclude< + PropertyNamesMatching, Func> | HelperNames>, + OptionalHelperNames> + > + > + > & + Partial>>> + > + : never; + + type Func = (...args: any[]) => any; + + // The names of all properties of T with either a HelperBrand or a HelperFlavor (whether required or optional) + type HelperNames = T extends T + ? { + [K in keyof T]: Exclude extends infer NoUndefined + ? [HelperBrand] extends [NoUndefined] + ? K + : [HelperBrand | OptionalHelperBrand] extends [NoUndefined] + ? K + : [Required] extends [Required] + ? K + : [Required] extends [Required] + ? K + : never + : never; + }[keyof T] + : never; + + // We also want to strip brands from the flavor-unsupported types - since the brands are no longer needed to + // tell us which types are helpers, * + type RemoveHelperBrands = { + [K in keyof T]: Exclude, HelperBrand | OptionalHelperBrand>; + }; + + // void is a bit of a weird case; unlike the others, it doesn't explicitly become never when flavored, + // and (void & HelperFlavor) *is* assignable to void, but only other expressions of type (void & HelperFlavor) + // are assignable to it + // however, this is just enough to let us support it with a bit of special-casing - convert it back to normal + // void when stripping helper brands, and Helpers.helperVoidProperty can be assigned undefined! + // This is one way to make a helper with an optional value that can be read off a raw TInterface (although + // Helper would work almost as well). + // tslint:disable-next-line void-return + type RemoveHelperFlavorForVoid = T extends void & HelperFlavor ? void : T; + + // however, we can do better + /** + * Use this to indicate a helper which can be omitted when providing helpers. + * Just marking the property as optional won't make it optional when providing helpers, as that's overloaded + * as meaning "an instance of this interface can be created from an object literal without providing its + * helpers". If you actually want a helper that's only sometimes there, use this. + */ + export type OptionalHelper = T extends T + ? T extends FlavorUnsupportedTypes + ? T | HelperBrand | OptionalHelperBrand | undefined + : (T & HelperFlavor & OptionalHelperFlavor) | undefined + : never; + interface OptionalHelperFlavor { + _meteor_dburles_collection_helpers_isOptionalHelper?: Flavor; + } + interface OptionalHelperBrand { + _meteor_dburles_collection_helpers_isBrandUnsupportedOptionalHelper?: Brand; + } + type OptionalHelperNames = T extends T + ? { + [K in keyof T]: Exclude extends infer NoUndefined + ? [HelperBrand | OptionalHelperBrand] extends [NoUndefined] + ? K + : [Required] extends [Required] + ? K + : never + : never; + }[keyof T] + : never; + + interface DataFlavor { + _meteor_dburles_collection_helpers_isData?: [Flavor, T]; + } + /** + * Just the non-method/Helper properties of the type, with the methods and Helpers made optional. + * No need to declare a Collection>; all Collection methods already accept a Data. + */ + export type Data = DataFlavor & NonHelpersOf & (T extends T ? Partial> : never); + /** + * NonData> === T + */ + export type NonData = T extends DataFlavor ? U : T; + + // All the members of T that aren't helpers + type NonHelpersOf = T extends T + ? Pick< + T, + Exclude< + PropertyNamesNotMatching, Func>, + HelperNames> | OptionalHelperNames> + > + > + : never; + + /** + * The version of a type that comes out of the collection (with helpers attached). + */ + export type Full = NonData> & (T extends T ? HelpersOf>> : never); + + type Brand = "_meteor_dburles_collection_helpers_brand"; + type Flavor = "_meteor_dburles_collection_helpers_flavor"; + + /** + * Collection.helpers() allows declaring only some of a collection's helpers, + * rather than enforcing that they be declared all at once. + * Only use this if you know what you're doing - it's assumed that all of a collection's helpers + * will be provided before any items are retrieved from it. + */ + export type AllowPartial = "_meteor_dburles_collection_helpers_allowPartial"; + /** + * Some subset of a collection's helpers - only used by Collection.helpers() + */ + export type PartialHelpers = ThisType>> & Partial>; +} diff --git a/types/meteor-dburles-collection-helpers/index.d.ts b/types/meteor-dburles-collection-helpers/index.d.ts new file mode 100644 index 0000000000..2842a34d62 --- /dev/null +++ b/types/meteor-dburles-collection-helpers/index.d.ts @@ -0,0 +1,8 @@ +// Type definitions for non-npm package Atmosphere package dburles:collection-helpers 1.1 +// Project: https://github.com/dburles/meteor-collection-helpers +// Definitions by: Artemis Kearney +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped +// Minimum TypeScript Version: 3.7 + +/// +/// diff --git a/types/meteor-dburles-collection-helpers/meteor-dburles-collection-helpers-tests.ts b/types/meteor-dburles-collection-helpers/meteor-dburles-collection-helpers-tests.ts new file mode 100644 index 0000000000..bad0ea9403 --- /dev/null +++ b/types/meteor-dburles-collection-helpers/meteor-dburles-collection-helpers-tests.ts @@ -0,0 +1,460 @@ +import { Mongo } from 'meteor/mongo'; +import { + Helper, + OptionalHelper, + Data, + Full, + Helpers, + AllowPartial, +} from 'meteor/dburles:collection-helpers'; + +interface Author { + _id?: string; + firstName: string; + lastName: string; + // function properties are automatically detected as helpers; no need to specify + fullName: () => string; + books: () => Mongo.Cursor; +} + +interface Book { + _id?: string; + authorId: string; + name: string; + author: () => Author | undefined; + // use Helper to declare non-function helpers + foo: Helper; +} + +const Books = new Mongo.Collection('books'); +const Authors = new Mongo.Collection('authors'); + +// $ExpectType Collection +Books; + +// when inserting items, only data properties are required +const author1 = Authors.insert({ + firstName: 'Charles', + lastName: 'Darwin', +}); + +const author2 = Authors.insert({ + firstName: 'Carl', + lastName: 'Sagan', +}); + +const book1 = Books.insert({ + authorId: author1, + name: 'On the Origin of Species', +}); + +const book2 = Books.insert({ + authorId: author2, + name: 'Contact', +}); + +// when providing helpers, no data properties but all helpers are required +// all helpers must be provided at once; this is the only way to typecheck that none are forgotten +// $ExpectType void +Books.helpers({ + author() { + return Authors.findOne(this.authorId); + }, + foo: 'bar', +}); +// $ExpectError +Books.helpers({ + author() { + return Authors.findOne(this.authorId); + }, +}); + +// you can override this behavior if you explicitly request to +// (don't do this unless you intend to provide all the helpers sooner or later!) +// $ExpectType void +Authors.helpers({ + fullName() { + return `${this.firstName} ${this.lastName}`; + }, +}); +// $ExpectType void +Authors.helpers({ + books() { + return Books.find({ authorId: this._id }); + }, +}); + +const book = Books.findOne(book1)!; +const author: Author | undefined = book.author(); + +// can modify resulting Book and update Books with it, even though it has helpers attached +book.name = 'Renamed Book'; +// $ExpectType number +Books.update(book._id!, book); + +// with mandatory helpers, new objects can be declared directly as Data +const bookData: Data = { + authorId: 'Author', + name: 'Name', +}; + +// this interface has its helpers declared as optional; this makes instantiating the interface easier, +// but means you need to specify Full to say an object has had its helpers attached +interface OptionalHelpers { + _id: string; + value: number; + increment?: () => void; + zero?: Helper; +} + +const optionalHelpers = new Mongo.Collection('optionalHelpers'); +// optional helpers still have to be provided when calling helpers +// $ExpectError +optionalHelpers.helpers({}); +// $ExpectError +optionalHelpers.helpers({ + increment() { + this.value++; + }, +}); +// $ExpectType void +optionalHelpers.helpers({ + increment() { + this.value++; + }, + zero: 0, +}); + +const optionalHelper1 = optionalHelpers.insert({ value: 2 }); +// Helpers are still guaranteed on the results of findOne, even though helpers were declared optional +// $ExpectType void +optionalHelpers.findOne(optionalHelper1)!.increment(); +// same goes for find +// $ExpectType void +optionalHelpers.find(optionalHelper1).fetch()[0].increment(); + +const foundOptionalHelpers1: OptionalHelpers = optionalHelpers.findOne(optionalHelper1)!; +// however, variables of the interface type will be missing their helpers unless declared as Full +// $ExpectError +foundOptionalHelpers1.increment(); + +// you can do this, but it's kinda ugly imo +const foundOptionalHelpers1Full: Full = optionalHelpers.findOne(optionalHelper1)!; +// $ExpectType void +foundOptionalHelpers1Full.increment(); + +// the benefit of this declaration style is that you can pass around instances without ever putting them in a collection: +const takesOptionalHelpers = (arg: OptionalHelpers) => { + return arg.value; +}; +const literalOptHelp: OptionalHelpers = { + _id: "id", + value: 4, +}; +// $ExpectType number +takesOptionalHelpers(literalOptHelp); +// $ExpectType number +takesOptionalHelpers({ + _id: "another id", + value: 13 +}); +// this might be a better choice if your interface is a general data type that you just happen to put in collections sometimes, +// rather than a collection schema you work with retrieved instances of often + +// helpers can call themselves recursively +interface RecursiveHelpers { + _id: string; + value: number; + factorial: (arg: number) => number; +} + +const recursiveHelpers = new Mongo.Collection('recursiveHelpers'); +recursiveHelpers.helpers({ + factorial(x) { + if (x <= 1) return 1; + return this.factorial(x - 1); + }, +}); + +const rh1 = recursiveHelpers.insert({ value: 3 }); +// $ExpectType number +recursiveHelpers.findOne(rh1)!.factorial(4); + +// even when optional! +interface RecursiveOptionalHelpers { + _id: string; + value: number; + factorial?: (arg: number) => number; +} + +const recursiveOptionalHelpers = new Mongo.Collection('recursiveHelpers'); +recursiveHelpers.helpers({ + factorial(x) { + if (x <= 1) return 1; + return this.factorial(x - 1); + }, +}); + +const roh1 = recursiveOptionalHelpers.insert({ value: 3 }); +// $ExpectType number +recursiveHelpers.findOne(rh1)!.factorial(4); + +// regression test: +// { ...OptionalId>, _id: T['id'] } should be assignable to Data +// (tested here with upsert) + +interface MandatoryId { + _id: string; + value: number; +} +const mandatoryIds = new Mongo.Collection('mandatoryIds'); +mandatoryIds.helpers({}); +const withoutId: Mongo.OptionalId = { + value: 3, +}; + +// $ExpectType number | undefined +mandatoryIds.upsert('new ID', { ...withoutId, _id: 'new ID' }).numberAffected; + +// regression test: +// union properties on interfaces: +// - are helpers if all their members are helpers +// - aren't helpers if none of their members are helpers +// - may or may not be helpers if only some of their members are helpers +// (consider this undefined behavior; I don't think there's *any* correct behavior +// for this situation, so I'm just letting it work out however it does without intervention) +// unions of Helper and unmarked methods don't work; if you legitimately need this functionality, +// tell me and I'll fix it; use Helper as a workaround until then +// +// null does not count as a helper type +// however, Helper and Helper should work more or less correctly +interface ComplicatedMembers { + _id?: string; + nullable: number | null; + alwaysNull: null; + helperNull: Helper; + helperNumber: Helper; + helperNullableFalse: Helper; + optionalHelperString?: Helper; + // tslint:disable-next-line void-return + helperVoidableNumber: Helper; + methodUnion: (() => boolean) | ((arg: number) => boolean); + helperUnion: Helper | Helper; + nonHelperUnion: number | string; + helperMethodOrString: Helper<(() => string) | string>; +} +const complicatedMembers = new Mongo.Collection('complicatedMembers'); + +// every member recognized as a helper is required when providing helpers +// $ExpectType void +complicatedMembers.helpers({ + methodUnion: () => true, + helperUnion: 3, + helperNumber: 5, + helperNullableFalse: null, + helperNull: null, + helperVoidableNumber: undefined, + optionalHelperString: 'foo', + helperMethodOrString: () => "method", +}); + +// $ExpectError +complicatedMembers.helpers({ + helperUnion: 3, + helperNumber: 5, + helperNullableFalse: null, + helperNull: null, + helperVoidableNumber: undefined, + optionalHelperString: 'foo', + helperMethodOrString: () => "method", +}); + +// $ExpectError +complicatedMembers.helpers({ + methodUnion: () => true, + helperNumber: 5, + helperNullableFalse: null, + helperNull: null, + helperVoidableNumber: undefined, + optionalHelperString: 'foo', + helperMethodOrString: () => "method", +}); + +// $ExpectError +complicatedMembers.helpers({ + methodUnion: () => true, + helperUnion: 3, + helperNullableFalse: null, + helperNull: null, + helperVoidableNumber: undefined, + optionalHelperString: 'foo', + helperMethodOrString: () => "method", +}); + +// $ExpectError +complicatedMembers.helpers({ + methodUnion: () => true, + helperUnion: 3, + helperNumber: 5, + helperNull: null, + helperVoidableNumber: undefined, + optionalHelperString: 'foo', + helperMethodOrString: () => "method", +}); + +// $ExpectError +complicatedMembers.helpers({ + methodUnion: () => true, + helperUnion: 3, + helperNumber: 5, + helperNullableFalse: null, + helperNull: null, + helperVoidableNumber: undefined, + helperMethodOrString: () => "method", +}); + +// $ExpectError +complicatedMembers.helpers({ + methodUnion: () => true, + helperUnion: 3, + helperNumber: 5, + helperNullableFalse: null, + helperVoidableNumber: undefined, + optionalHelperString: 'foo', + helperMethodOrString: () => "method", +}); + +// $ExpectError +complicatedMembers.helpers({ + methodUnion: () => true, + helperUnion: 3, + helperNumber: 5, + helperNullableFalse: null, + helperNull: null, + optionalHelperString: 'foo', + helperMethodOrString: () => "method", +}); + +// $ExpectError +complicatedMembers.helpers({ + methodUnion: () => true, + helperUnion: 3, + helperNumber: 5, + helperNullableFalse: null, + helperVoidableNumber: undefined, + helperNull: null, + optionalHelperString: 'foo', +}); + +const complicatedMembersId = complicatedMembers.insert({ + nullable: 2, + alwaysNull: null, + nonHelperUnion: 'test', +}); + +const complicatedMembersInstance = complicatedMembers.findOne(complicatedMembersId)!; +// $ExpectType number +complicatedMembersInstance.helperNumber + 1; +// $ExpectType string +complicatedMembersInstance.optionalHelperString.slice(); +// $ExpectType boolean +complicatedMembersInstance.methodUnion(3); +const strOrNum: string | number = complicatedMembersInstance.helperUnion; +const falseOrNull: false | null = complicatedMembersInstance.helperNullableFalse; +const helperNullAccess: null = complicatedMembersInstance.helperNull; + +// Limitation: null/undefined helpers, and helpers whose types are unions with null or undefined, can't be properly +// read off a raw ComplicatedMembers +const asComplicatedMembers: ComplicatedMembers = complicatedMembersInstance; + +// const falseOrNull2 : false | null = asComplicatedMembers.helperNullableFalse; + +// you can work with these by: +// - accepting Helper rather than null +const falseOrNull2: false | Helper = asComplicatedMembers.helperNullableFalse; + +// - using a "voidable" rather than a nullable +// tslint:disable-next-line void-return +const numberOrVoid: number | void = asComplicatedMembers.helperVoidableNumber; + +// - or, the recommended solution: using OptionalHelper +// OptionalHelper defines a helper which can be left undefined when providing a collection's helpers +// that property can be required or optional on the interface; either way, it'll be optional on Helpers and +// on Full +// and it even works on methods +interface ActuallyOptionalHelpers { + _id?: string; + value: number; + getValue: OptionalHelper<() => number>; + incrementValue?: OptionalHelper<() => void>; + optionalHelperValue: OptionalHelper; + optionalHelperName?: OptionalHelper; +} +const actuallyOptionalHelpers = new Mongo.Collection('actuallyOptionalHelpers'); + +// every one of those helpers is totally optional - we can even provide an empty object! +// $ExpectType void +actuallyOptionalHelpers.helpers({}); +// $ExpectType void +actuallyOptionalHelpers.helpers({ + getValue() { + return this.value; + }, +}); +// $ExpectType void +actuallyOptionalHelpers.helpers({ + incrementValue() { + this.value++; + }, +}); +// $ExpectType void +actuallyOptionalHelpers.helpers({ + optionalHelperValue: 3, +}); +// $ExpectType void +actuallyOptionalHelpers.helpers({ + optionalHelperName: 'foo', +}); +// $ExpectType void +actuallyOptionalHelpers.helpers({ + getValue() { + return this.value; + }, + incrementValue() { + this.value++; + }, + optionalHelperValue: 3, + optionalHelperName: 'foo', +}); + +// as usual, only non-helper properties are required when inserting an item +const aohId = actuallyOptionalHelpers.insert({ + value: 7, +}); + +const aohInstance = actuallyOptionalHelpers.findOne(aohId)!; + +// since they don't have to be provided, users of an item with optional helpers aren't promised those helpers will be present +let aohName: string | undefined = aohInstance.optionalHelperName; +const aohValue: number | undefined = aohInstance.optionalHelperValue; +// $ExpectError +aohInstance.getValue(); + +// asserting their existence works +// $ExpectType number +aohInstance.getValue!(); +// as does control-flow analysis +if (aohInstance.getValue) { + // $ExpectType number + aohInstance.getValue(); +} + +// can still put retrieved items back into the container +actuallyOptionalHelpers.insert(aohInstance); + +// unlike with Helper, these work directly from the original interface type +const asAoh: ActuallyOptionalHelpers = aohInstance; + +aohName = asAoh.optionalHelperName; +// $ExpectType number +asAoh.getValue!(); diff --git a/types/meteor-dburles-collection-helpers/mongo.d.ts b/types/meteor-dburles-collection-helpers/mongo.d.ts new file mode 100644 index 0000000000..cba21ea670 --- /dev/null +++ b/types/meteor-dburles-collection-helpers/mongo.d.ts @@ -0,0 +1,103 @@ +import { Helpers, Full, Data, AllowPartial, PartialHelpers } from 'meteor/dburles:collection-helpers'; +import { Meteor } from 'meteor/meteor'; +import { Mongo } from 'meteor/mongo'; + +// Cursor and Collection are pulled from https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/meteor/mongo.d.ts +// and should be kept in sync with any changes to it +// only modified properties are included + +// tslint:disable-next-line no-single-declare-module +declare module 'meteor/mongo' { + namespace Mongo { + interface Collection { + /** + * Provide the definitions here for the methods and Helpers you declared on your item interface. + * Use helpers if you want to provide the helpers across multiple calls. + * Tip: If you make those properties non-optional, they still won't be required when inserting items, + * but you'll know that any object of your interface type has them. + * Alternatively, if they're marked optional on your interface, they'll still be guaranteed on any + * object you get out of the collection. + * If you plan to pass around and create a lot of items pre-insertion, make them optional and use Full + * for ones with helpers attached. + * If you plan to mostly pass around items that came out of a collection, make them required and use Data + * when creating new items. + */ + helpers( + // tslint:disable-next-line no-unnecessary-generics + helpers: (allowPartial extends AllowPartial ? PartialHelpers : Helpers) + ): void; + + // modifications: + // - replaced T with Full & T everywhere Collection._transform is applied + // - replaced T with Data everywhere the user provides a T + + allow(options: { + insert?: (userId: string, doc: Full & T) => boolean; + update?: (userId: string, doc: Full & T, fieldNames: string[], modifier: any) => boolean; + remove?: (userId: string, doc: Full & T) => boolean; + fetch?: string[]; + // ditto + // tslint:disable-next-line ban-types + transform?: Function | null; + }): boolean; + deny(options: { + insert?: (userId: string, doc: Full & T) => boolean; + update?: (userId: string, doc: Full & T, fieldNames: string[], modifier: any) => boolean; + remove?: (userId: string, doc: Full & T) => boolean; + fetch?: string[]; + // ditto + // tslint:disable-next-line ban-types + transform?: Function | null; + }): boolean; + findOne( + selector?: Selector | ObjectID | string, + options?: { + sort?: SortSpecifier; + skip?: number; + fields?: FieldSpecifier; + reactive?: boolean; + // ditto + // tslint:disable-next-line ban-types + transform?: Function | null; + }, + ): (Full & T) | undefined; + // ditto + // tslint:disable-next-line ban-types + insert(doc: OptionalId>, callback?: Function): string; + update( + selector: Selector | ObjectID | string, + modifier: Modifier>, + options?: { + multi?: boolean; + upsert?: boolean; + arrayFilters?: Array<{ [identifier: string]: any }>; + }, + // ditto + // tslint:disable-next-line ban-types + callback?: Function, + ): number; + upsert( + selector: Selector | ObjectID | string, + modifier: Modifier>, + options?: { + multi?: boolean; + }, + // ditto + // tslint:disable-next-line ban-types + callback?: Function, + ): { + numberAffected?: number; + insertedId?: string; + }; + } + + // modifications: replaced T with Full & T everywhere Collection._transform is applied + // note: it's not applied for observeChanges; however, ObserveChangesCallbacks uses Partial anyway + interface Cursor { + fetch(): Array & T>; + forEach(callback: (doc: Full & T, index: number, cursor: Cursor) => void, thisArg?: any): void; + map(callback: (doc: Full & T, index: number, cursor: Cursor) => U, thisArg?: any): U[]; + observe(callbacks: ObserveCallbacks & T>): Meteor.LiveQueryHandle; + } + } +} diff --git a/types/meteor-dburles-collection-helpers/tsconfig.json b/types/meteor-dburles-collection-helpers/tsconfig.json new file mode 100644 index 0000000000..d24bf2cad7 --- /dev/null +++ b/types/meteor-dburles-collection-helpers/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "module": "commonjs", + "lib": [ + "es6", + "dom" + ], + "noImplicitAny": true, + "noImplicitThis": true, + "strictFunctionTypes": true, + "strictNullChecks": true, + "baseUrl": "../", + "typeRoots": [ + "../" + ], + "types": [], + "noEmit": true, + "forceConsistentCasingInFileNames": true + }, + "files": [ + "index.d.ts", + "meteor-dburles-collection-helpers-tests.ts" + ] +} diff --git a/types/meteor-dburles-collection-helpers/tslint.json b/types/meteor-dburles-collection-helpers/tslint.json new file mode 100644 index 0000000000..3db14f85ea --- /dev/null +++ b/types/meteor-dburles-collection-helpers/tslint.json @@ -0,0 +1 @@ +{ "extends": "dtslint/dt.json" }