diff --git a/README.md b/README.md index 9785e3c77085..7f0ee0680612 100644 --- a/README.md +++ b/README.md @@ -135,7 +135,7 @@ ctrl.fetch(ArticleResource.get, { id }); ### [Programmatic queries](https://dataclient.io/rest/api/Query) ```tsx -const queryTotalVotes = new Query( +const queryTotalVotes = new schema.Query( new schema.All(Post), (posts, { userId } = {}) => { if (userId !== undefined) @@ -144,8 +144,8 @@ const queryTotalVotes = new Query( }, ); -const totalVotes = useCache(queryTotalVotes); -const totalVotesForUser = useCache(queryTotalVotes, { userId }); +const totalVotes = useQuery(queryTotalVotes); +const totalVotesForUser = useQuery(queryTotalVotes, { userId }); ``` ### [Powerful Middlewares](https://dataclient.io/docs/concepts/managers) diff --git a/__tests__/new.ts b/__tests__/new.ts index 00b1d837c5e4..ee16d7a7bf80 100644 --- a/__tests__/new.ts +++ b/__tests__/new.ts @@ -2,7 +2,6 @@ import { schema, AbortOptimistic, Endpoint, - Index, createResource, RestEndpoint, Schema, @@ -519,13 +518,10 @@ export const CoolerArticleDetail = new Endpoint( export class IndexedUser extends User { static readonly indexes = ['username']; } -export const IndexedUserResource = { - ...createResource({ - path: 'http\\://test.com/user/:id', - schema: IndexedUser, - }), - getIndex: new Index(IndexedUser), -}; +export const IndexedUserResource = createResource({ + path: 'http\\://test.com/user/:id', + schema: IndexedUser, +}); class InvalidIfStaleEndpoint< O extends RestGenerics = any, diff --git a/docs/core/api/useCache.md b/docs/core/api/useCache.md index b89b158ac35d..61b4b893fde1 100644 --- a/docs/core/api/useCache.md +++ b/docs/core/api/useCache.md @@ -181,10 +181,11 @@ export const UserResource = createResource({ ``` ```tsx title="UsersPage" {17} -import { Query, schema } from '@data-client/rest'; +import { schema } from '@data-client/rest'; +import { useQuery, useFetch } from '@data-client/react'; import { UserResource, User } from './UserResource'; -const sortedUsers = new Query( +const sortedUsers = new schema.Query( new schema.All(User), (entries, { asc } = { asc: false }) => { const sorted = [...entries].sort((a, b) => @@ -197,7 +198,7 @@ const sortedUsers = new Query( function UsersPage() { useFetch(UserResource.getList); - const users = useCache(sortedUsers, { asc: true }); + const users = useQuery(sortedUsers, { asc: true }); if (!users) return
No users in cache yet
; return (
diff --git a/docs/core/shared/_VoteDemo.mdx b/docs/core/shared/_VoteDemo.mdx index 7533617ceaaa..0810e89c4bbb 100644 --- a/docs/core/shared/_VoteDemo.mdx +++ b/docs/core/shared/_VoteDemo.mdx @@ -82,10 +82,11 @@ export default function PostItem({ post }: { post: Post }) { ``` ```tsx title="TotalVotes" collapsed -import { Query, schema } from '@data-client/rest'; +import { schema } from '@data-client/rest'; +import { useQuery } from '@data-client/rest'; import { Post } from './PostResource'; -const queryTotalVotes = new Query( +const queryTotalVotes = new schema.Query( new schema.All(Post), (posts, { userId } = {}) => { if (userId !== undefined) @@ -95,7 +96,7 @@ const queryTotalVotes = new Query( ); export default function TotalVotes({ userId }: { userId: number }) { - const totalVotes = useCache(queryTotalVotes, { userId }); + const totalVotes = useQuery(queryTotalVotes, { userId }); return (
{totalVotes} votes total diff --git a/docs/rest/api/Entity.md b/docs/rest/api/Entity.md index f480175fcf32..df1f844575f1 100644 --- a/docs/rest/api/Entity.md +++ b/docs/rest/api/Entity.md @@ -562,9 +562,9 @@ export const UserResource = createResource({ const user = useSuspense(UserResource.get, { username: 'bob' }); ``` -#### useCache() +#### useQuery() -With [useCache()](/docs/api/useCache), this enables accessing results retrieved inside other requests - even +With [useQuery()](/docs/api/useQuery), this enables accessing results retrieved inside other requests - even if there is no endpoint it can be fetched from. ```typescript @@ -574,7 +574,6 @@ class LatestPrice extends Entity { price = '0.0'; static indexes = ['symbol' as const]; } -const latestPriceFromCache = new Index(LatestPrice); ``` ```typescript @@ -601,5 +600,5 @@ const assets = useSuspense(getAssets); Nested below: ```tsx -const price = useCache(latestPriceFromCache, { symbol: 'BTC' }); +const price = useQuery(LatestPrice, { symbol: 'BTC' }); ``` diff --git a/docs/rest/api/Index.md b/docs/rest/api/Index.md deleted file mode 100644 index b1c601a4f8b1..000000000000 --- a/docs/rest/api/Index.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -title: Index -slug: IndexEndpoint ---- - -```typescript -export interface IndexInterface { - key(params?: P): string; - readonly schema: S; -} -``` - -```typescript -import { Entity } from '@data-client/normalizr'; -import { Index } from '@data-client/endpoint'; - -class User extends Entity { - readonly id: string = ''; - readonly username: string = ''; - - pk() { return this.id;} - static indexes = ['username'] as const; -} - -const UserIndex = new Index(User) - -const bob = useCache(UserIndex, { username: 'bob' }); - -// @ts-expect-error Indexes don't fetch, they just retrieve already existing data -const bob = useSuspense(UserIndex, { username: 'bob' }); -``` diff --git a/docs/rest/api/Query.md b/docs/rest/api/Query.md index 8125c811e839..001be82b65af 100644 --- a/docs/rest/api/Query.md +++ b/docs/rest/api/Query.md @@ -1,6 +1,5 @@ --- -title: Query -slug: Query +title: schema.Query --- @@ -14,94 +13,22 @@ import HooksPlayground from '@site/src/components/HooksPlayground'; `Query` provides programmatic access to the Reactive Data Client cache while maintaining the same high performance and referential equality guarantees expected of Reactive Data Client. -```typescript -class Query { - constructor( - schema: S, - process?: (entries: Denormalize, ...args: P) => Denormalize, - ); - - schema: S; - key(...args: P): string; - - process: (entries: Denormalize, ...args: P) => Denormalize; -} -``` - -`Query` implements the [EndpointInterface](./Endpoint.md) but without the fetch function, which -means it can only be passed to the [data binding hook useCache()](/docs/api/useCache) +`Query` can be rendered using [schema lookup hook useQuery()](/docs/api/useQuery()) ## Query members ### schema [Schema](./schema.md) used to retrieve/denormalize data from the Reactive Data Client cache. -Most cases will use [schema.All](./All.md), which retrieves all entities of a given type found -in the cache. +This accepts any Queryable schema. ### process(entries, ...args) {#process} Takes the (denormalized) response as entries and arguments and returns the new -response for use with [useCache](/docs/api/useCache) - -### key(...args) {#key} - -Implements [Endpoint.key](./Endpoint.md#key) Used to determine recomputation of memoized values. +response for use with [useQuery](/docs/api/useQuery) ## Usage -### Simplest - - - -```ts title="api/User.ts" collapsed -export class User extends Entity { - id = ''; - name = ''; - isAdmin = false; - pk() { - return this.id; - } -} -export const UserResource = createResource({ - path: '/users/:id', - schema: User, -}); -``` - -```tsx title="UsersPage.tsx" {4} -import { Query, schema } from '@data-client/rest'; -import { UserResource, User } from './api/User'; - -const allUsers = new Query(new schema.All(User)); - -function UsersPage() { - useFetch(UserResource.getList); - const users = useCache(allUsers); - if (!users) return
No users in cache yet
; - return ( -
- {users.map(user => ( -
{user.name}
- ))} -
- ); -} -render(); -``` - -
- ### Sorting & Filtering { let sorted = [...entries].sort((a, b) => a.name.localeCompare(b.name)); @@ -153,7 +81,7 @@ const sortedUsers = new Query( function UsersPage() { useFetch(UserResource.getList); - const users = useCache(sortedUsers, { asc: true }); + const users = useQuery(sortedUsers, { asc: true }); if (!users) return
No users in cache yet
; return (
@@ -199,10 +127,11 @@ export const UserResource = createResource({ ``` ```tsx title="UsersPage" -import { Query, schema } from '@data-client/rest'; +import { schema } from '@data-client/rest'; +import { useQuery, useFetch } from '@data-client/react'; import { UserResource, User } from './api/User'; -const getUserCount = new Query( +const getUserCount = new schema.Query( new schema.All(User), (entries, { isAdmin } = { }) => { if (isAdmin !== undefined) @@ -213,8 +142,8 @@ const getUserCount = new Query( function UsersPage() { useFetch(UserResource.getList); - const userCount = useCache(getUserCount); - const adminCount = useCache(getUserCount, { isAdmin: true }); + const userCount = useQuery(getUserCount); + const adminCount = useQuery(getUserCount, { isAdmin: true }); if (userCount === undefined) return
No users in cache yet
; return (
@@ -279,11 +208,12 @@ export const TodoResource = createResource({ ``` ```tsx title="TodoJoined.tsx" -import { Query, schema } from '@data-client/rest'; +import { schema } from '@data-client/rest'; +import { useQuery, useFetch } from '@data-client/react'; import { TodoResource, Todo } from './api/Todo'; import { UserResource, User } from './api/User'; -const todosWithUser = new Query( +const todosWithUser = new schema.Query( new schema.All(Todo), (entries, { userId = 0 }) => { return entries.filter(todo => todo.userId?.id === userId); @@ -293,7 +223,7 @@ const todosWithUser = new Query( function TodosPage() { useFetch(UserResource.getList); useFetch(TodoResource.getList); - const todos = useCache(todosWithUser, { userId: 1 }); + const todos = useQuery(todosWithUser, { userId: 1 }); if (!todos) return
No Todos in cache yet
; return (
diff --git a/docs/rest/guides/computed-properties.md b/docs/rest/guides/computed-properties.md index 6aeddd6fc9ad..5a73b7bffdb8 100644 --- a/docs/rest/guides/computed-properties.md +++ b/docs/rest/guides/computed-properties.md @@ -113,10 +113,11 @@ export const UserResource = createResource({ ``` ```tsx title="UsersPage" -import { Query, schema } from '@data-client/rest'; +import { schema } from '@data-client/rest'; +import { useQuery, useFetch } from '@data-client/react'; import { UserResource, User } from './api/User'; -const getUserCount = new Query( +const getUserCount = new schema.Query( new schema.All(User), (entries, { isAdmin } = {}) => { if (isAdmin !== undefined) @@ -127,8 +128,8 @@ const getUserCount = new Query( function UsersPage() { useFetch(UserResource.getList); - const userCount = useCache(getUserCount); - const adminCount = useCache(getUserCount, { isAdmin: true }); + const userCount = useQuery(getUserCount); + const adminCount = useQuery(getUserCount, { isAdmin: true }); if (userCount === undefined) return
No users in cache yet
; return ( diff --git a/packages/core/src/controller/__tests__/__snapshots__/query.ts.snap b/packages/core/src/controller/__tests__/__snapshots__/query.ts.snap index ba75377606fd..1f6a41685eb4 100644 --- a/packages/core/src/controller/__tests__/__snapshots__/query.ts.snap +++ b/packages/core/src/controller/__tests__/__snapshots__/query.ts.snap @@ -74,6 +74,13 @@ exports[`Controller.query() query Collection based on args 2`] = ` ] `; +exports[`Controller.query() query Entity based on index 1`] = ` +User { + "id": "1", + "username": "bob", +} +`; + exports[`Controller.query() query Entity based on pk 1`] = ` Tacos { "id": "1", diff --git a/packages/core/src/controller/__tests__/query.ts b/packages/core/src/controller/__tests__/query.ts index 7b47f164d84c..de67facf7ab9 100644 --- a/packages/core/src/controller/__tests__/query.ts +++ b/packages/core/src/controller/__tests__/query.ts @@ -49,6 +49,43 @@ describe('Controller.query()', () => { () => controller.query(Tacos, { doesnotexist: 5 }, state); }); + it('query Entity based on index', () => { + class User extends Entity { + id = ''; + username = ''; + + pk() { + return this.id; + } + + static indexes = ['username'] as const; + } + + const controller = new Contoller(); + const state = { + ...initialState, + entities: { + User: { + '1': { id: '1', username: 'bob' }, + }, + }, + indexes: { + User: { + username: { + bob: '1', + }, + }, + }, + }; + + const bob = controller.query(User, { username: 'bob' }, state); + expect(bob).toBeDefined(); + expect(bob).toBeInstanceOf(User); + expect(bob).toMatchSnapshot(); + // should be same as id lookup + expect(bob).toBe(controller.query(User, { id: '1' }, state)); + }); + it('query Collection based on args', () => { const controller = new Contoller(); const state = { diff --git a/packages/endpoint/README.md b/packages/endpoint/README.md index 47fd6a91bdc3..31815f612346 100644 --- a/packages/endpoint/README.md +++ b/packages/endpoint/README.md @@ -205,13 +205,6 @@ const UserDetailNormalized = UserDetail.extend({ schema: User }); ### Index -```typescript -export interface IndexInterface { - key(parmas?: Readonly>): string; - readonly schema: S; -} -``` - ```typescript import { Entity } from '@data-client/normalizr'; import { Index } from '@data-client/endpoint'; @@ -224,9 +217,7 @@ class User extends Entity { static indexes = ['username'] as const; } -const UserIndex = new Index(User) - -const bob = useCache(UserIndex, { username: 'bob' }); +const bob = useQuery(User, { username: 'bob' }); // @ts-expect-error Indexes don't fetch, they just retrieve already existing data const bob = useSuspense(UserIndex, { username: 'bob' }); diff --git a/packages/endpoint/src/__tests__/indexEndpoint.ts b/packages/endpoint/src/__tests__/indexEndpoint.ts deleted file mode 100644 index f2bbb151b021..000000000000 --- a/packages/endpoint/src/__tests__/indexEndpoint.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { Index } from '../indexEndpoint'; -import Entity from '../schemas/Entity'; - -describe('Index', () => { - class User extends Entity { - readonly id: string = ''; - readonly username: string = ''; - - pk() { - return this.id; - } - - static indexes = ['username'] as const; - } - - it('constructs', () => { - const UserIndex = new Index(User); - - expect(UserIndex.key({ username: 'hi' })).toMatchInlineSnapshot( - `"{"username":"hi"}"`, - ); - - //@ts-expect-error - UserIndex.key({ username: 5 }); - //@ts-expect-error - UserIndex.key({ blah: 5 }); - }); - - it('constructs with key', () => { - const UserIndex = new Index(User, ({ username }) => `${username}`); - - expect(UserIndex.key({ username: 'hi' })).toMatchInlineSnapshot(`"hi"`); - - //@ts-expect-error - UserIndex.key({ username: 5 }); - //@ts-expect-error - UserIndex.key({ blah: 5 }); - }); -}); diff --git a/packages/endpoint/src/index.ts b/packages/endpoint/src/index.ts index 9b56e1159ce9..7a2cdd78d3f7 100644 --- a/packages/endpoint/src/index.ts +++ b/packages/endpoint/src/index.ts @@ -49,5 +49,4 @@ export type { export { default as Endpoint, ExtendableEndpoint } from './endpoint.js'; export type { KeyofEndpointInstance } from './endpoint.js'; -export * from './indexEndpoint.js'; export { default as AbortOptimistic } from './AbortOptimistic.js'; diff --git a/packages/endpoint/src/indexEndpoint.ts b/packages/endpoint/src/indexEndpoint.ts deleted file mode 100644 index 4c59dabd9a68..000000000000 --- a/packages/endpoint/src/indexEndpoint.ts +++ /dev/null @@ -1,35 +0,0 @@ -import type { Schema } from './interface.js'; -import type { AbstractInstanceType } from './normal.js'; - -/** - * Performant lookups by secondary indexes - * @see https://dataclient.io/docs/api/Index - */ -export class Index>> { - declare schema: S; - constructor(schema: S, key?: (params: P) => string) { - this.schema = schema; - if (key) this.key = key; - } - - key(params?: P) { - return JSON.stringify(params); - } -} - -export type ArrayElement = - ArrayType[number]; - -export type IndexParams = - S extends ( - { - indexes: readonly string[]; - } - ) ? - { - [K in Extract< - ArrayElement, - keyof AbstractInstanceType - >]?: AbstractInstanceType[K]; - } - : Readonly; diff --git a/packages/endpoint/src/queryEndpoint.ts b/packages/endpoint/src/queryEndpoint.ts deleted file mode 100644 index e08c6bc4a14e..000000000000 --- a/packages/endpoint/src/queryEndpoint.ts +++ /dev/null @@ -1,75 +0,0 @@ -import type { - EntityTable, - NormalizedIndex, - Queryable, - SchemaSimple, -} from './interface.js'; -import type { Denormalize, SchemaArgs } from './normal.js'; - -/** - * Programmatic cache reading - * @see https://dataclient.io/rest/api/Query - */ -export class Query< - S extends Queryable, - P extends SchemaArgs = SchemaArgs, - R = Denormalize, -> { - declare schema: QuerySchema; - // TODO: allow arbitrary return types then inferring it from - declare process: (entries: Denormalize, ...args: P) => R; - - constructor(schema: S, process?: (entries: Denormalize, ...args: P) => R) { - this.schema = this.createQuerySchema(schema); - if (process) this.process = process; - // allows for inheritance overrides - else if (!this.process) - this.process = ((entries: Denormalize) => entries) as any; - } - - key(...args: P) { - return `QUERY ${JSON.stringify(args)}`; - } - - protected createQuerySchema(schema: S) { - const query = Object.create(schema); - query.denormalize = (input: any, args: P, unvisit: any) => { - const value = unvisit(input, schema); - return typeof value === 'symbol' ? undefined : ( - this.process(value, ...args) - ); - }; - // do not look like an entity - query.pk = undefined; - query.infer = ( - args: any, - indexes: any, - recurse: ( - schema: any, - args: any[], - indexes: NormalizedIndex, - entities: EntityTable, - ) => any, - entities: EntityTable, - ) => { - return recurse(schema, args, indexes, entities); - }; - return query; - } -} - -type QuerySchema = Exclude< - Schema, - 'denormalize' | '_denormalizeNullable' -> & { - _denormalizeNullable( - input: {}, - args: readonly any[], - unvisit: (input: any, schema: any) => any, - ): R | undefined; - denormalize( - input: {}, - args: readonly any[], - unvisit: (input: any, schema: any) => any, - ): R; -}; diff --git a/packages/endpoint/src/schema.d.ts b/packages/endpoint/src/schema.d.ts index 54b13f1c8443..463a4d6f3e8f 100644 --- a/packages/endpoint/src/schema.d.ts +++ b/packages/endpoint/src/schema.d.ts @@ -127,9 +127,7 @@ export class All< | (S extends EntityMap ? UnionResult : Normalize)[] | undefined; - _denormalizeNullable(): - | (S extends EntityMap ? T : Denormalize)[] - | undefined; + _denormalizeNullable(): (S extends EntityMap ? T : Denormalize)[]; denormalize( input: {}, @@ -323,7 +321,7 @@ export declare class Collection< Args extends any[] = | [] | [urlParams: Record] - | [urlParams: Record, body: Record], + | [urlParams: Record, body: any], Parent = any, > extends CollectionRoot {} diff --git a/packages/endpoint/src/schemaTypes.ts b/packages/endpoint/src/schemaTypes.ts index 4d21205b7754..06bbf0653368 100644 --- a/packages/endpoint/src/schemaTypes.ts +++ b/packages/endpoint/src/schemaTypes.ts @@ -133,7 +133,7 @@ export type CollectionFromSchema< Args extends any[] = | [] | [urlParams: Record] - | [urlParams: Record, body: Record], + | [urlParams: Record, body: any], Parent = any, > = CollectionInterface< S extends any[] ? schema.Array : S, @@ -147,7 +147,7 @@ export interface CollectionConstructor { Args extends any[] = | [] | [urlParams: Record] - | [urlParams: Record, body: Record], + | [urlParams: Record, body: any], Parent = any, >( schema: S, diff --git a/packages/endpoint/src/schemas/All.ts b/packages/endpoint/src/schemas/All.ts index dbb9056251f3..bdc6e61e7683 100644 --- a/packages/endpoint/src/schemas/All.ts +++ b/packages/endpoint/src/schemas/All.ts @@ -1,6 +1,7 @@ import ArraySchema from './Array.js'; import { EntityTable } from '../interface.js'; import { EntityInterface, EntityMap, SchemaFunction } from '../schema.js'; +import { INVALID } from '../special.js'; /** * Retrieves all entities in cache @@ -42,6 +43,18 @@ export default class AllSchema< ); } + denormalize( + input: any, + args: any[], + unvisit: (input: any, schema: any) => any, + ) { + // input is only empty if no entity table exists. + // however, empty entity table will still return [] + // this way, denormalize will never be undefined - only INVALID or an Array + if (!input) return INVALID; + return super.denormalize(input, args, unvisit); + } + infer(args: any, indexes: any, recurse: any, entities: EntityTable): any { if (this.isSingleSchema) { const entitiesEntry = entities[this.schema.key]; diff --git a/packages/endpoint/src/schemas/Collection.ts b/packages/endpoint/src/schemas/Collection.ts index d0b4957e45df..882b5b2e7390 100644 --- a/packages/endpoint/src/schemas/Collection.ts +++ b/packages/endpoint/src/schemas/Collection.ts @@ -24,7 +24,7 @@ export default class CollectionSchema< Args extends any[] = | [] | [urlParams: Record] - | [urlParams: Record, body: Record], + | [urlParams: Record, body: any], Parent = any, > { protected declare nestKey: (parent: any, key: string) => Record; @@ -240,7 +240,7 @@ export type CollectionOptions< Args extends any[] = | [] | [urlParams: Record] - | [urlParams: Record, body: Record], + | [urlParams: Record, body: any], Parent = any, > = ( | { diff --git a/packages/endpoint/src/schemas/Query.ts b/packages/endpoint/src/schemas/Query.ts index f45f414ac563..1296216b968b 100644 --- a/packages/endpoint/src/schemas/Query.ts +++ b/packages/endpoint/src/schemas/Query.ts @@ -4,7 +4,7 @@ import type { Queryable, SchemaSimple, } from '../interface.js'; -import type { Denormalize, SchemaArgs } from '../normal.js'; +import type { DenormalizeNullable, SchemaArgs } from '../normal.js'; /** * Programmatic cache reading @@ -12,7 +12,7 @@ import type { Denormalize, SchemaArgs } from '../normal.js'; */ export default class Query< S extends Queryable, - P extends (entries: Denormalize, ...args: any) => any, + P extends (entries: DenormalizeNullable, ...args: any) => any, > implements SchemaSimple | undefined, ProcessParameters> { declare schema: S; diff --git a/packages/endpoint/src/schemas/__tests__/Collection.test.ts b/packages/endpoint/src/schemas/__tests__/Collection.test.ts index f3e964683461..27261d1e81f9 100644 --- a/packages/endpoint/src/schemas/__tests__/Collection.test.ts +++ b/packages/endpoint/src/schemas/__tests__/Collection.test.ts @@ -251,15 +251,7 @@ describe(`${schema.Collection.name} normalization`, () => { state.entityMeta, ), }; - function validate( - sch: schema.Collection< - (typeof Todo)[], - [ - urlParams: globalThis.Record, - body?: globalThis.Record | undefined, - ] - >, - ) { + function validate(sch: schema.Collection<(typeof Todo)[]>) { expect( ( denormalize( @@ -327,7 +319,7 @@ describe(`${schema.Collection.name} normalization`, () => { ([key, value]) => key.startsWith('ignored') || // strings are canonical form. See pk() above for value transformation - `${args[0][key]}` === value || + `${args[0]?.[key]}` === value || `${args[1]?.[key]}` === value, ), }); @@ -336,7 +328,10 @@ describe(`${schema.Collection.name} normalization`, () => { it('should work with function override of nonFilterArgumentKeys', () => { class MyCollection< S extends any[] | PolymorphicInterface = any, - Parent extends any[] = [urlParams: any, body?: any], + Parent extends any[] = + | [] + | [urlParams: { [k: string]: any }] + | [urlParams: { [k: string]: any }, body: { [k: string]: any }], > extends schema.Collection { nonFilterArgumentKeys(key: string) { return key.startsWith('ignored'); @@ -348,7 +343,10 @@ describe(`${schema.Collection.name} normalization`, () => { it('should work with function override of createCollectionFilter', () => { class MyCollection< S extends any[] | PolymorphicInterface = any, - Parent extends any[] = [urlParams: any, body?: any], + Parent extends any[] = + | [] + | [urlParams: { [k: string]: any }] + | [urlParams: { [k: string]: any }, body: { [k: string]: any }], > extends schema.Collection { createCollectionFilter(...args: Parent) { return (collectionKey: { [k: string]: string }) => diff --git a/packages/endpoint/src/schemas/__tests__/Query.test.ts b/packages/endpoint/src/schemas/__tests__/Query.test.ts index 5e4bcdb024a4..48f0afd9d8cf 100644 --- a/packages/endpoint/src/schemas/__tests__/Query.test.ts +++ b/packages/endpoint/src/schemas/__tests__/Query.test.ts @@ -131,6 +131,7 @@ describe.each([ const userCountByAdmin = new schema.Query( usersSchema, ({ results }, { isAdmin }: { isAdmin?: boolean } = {}) => { + if (!results) return 0; if (isAdmin === undefined) return results.length; return results.filter(user => user.isAdmin === isAdmin).length; }, diff --git a/packages/react/README.md b/packages/react/README.md index 8400e486128f..aaaf06bca5e0 100644 --- a/packages/react/README.md +++ b/packages/react/README.md @@ -129,7 +129,7 @@ ctrl.fetch(ArticleResource.get, { id }); ### [Programmatic queries](https://dataclient.io/rest/api/Query) ```tsx -const queryTotalVotes = new Query( +const queryTotalVotes = new schema.Query( new schema.All(Post), (posts, { userId } = {}) => { if (userId !== undefined) @@ -138,8 +138,8 @@ const queryTotalVotes = new Query( }, ); -const totalVotes = useCache(queryTotalVotes); -const totalVotesForUser = useCache(queryTotalVotes, { userId }); +const totalVotes = useQuery(queryTotalVotes); +const totalVotesForUser = useQuery(queryTotalVotes, { userId }); ``` ### [Powerful Middlewares](https://dataclient.io/docs/concepts/managers) diff --git a/packages/react/src/__tests__/integration-index-endpoint.web.tsx b/packages/react/src/__tests__/integration-index-endpoint.web.tsx index b185dd948cc6..a3d41414084d 100644 --- a/packages/react/src/__tests__/integration-index-endpoint.web.tsx +++ b/packages/react/src/__tests__/integration-index-endpoint.web.tsx @@ -1,5 +1,5 @@ import { act } from '@testing-library/react-hooks'; -import { IndexedUserResource, User } from '__tests__/new'; +import { IndexedUser, IndexedUserResource, User } from '__tests__/new'; import nock from 'nock'; import { useContext } from 'react'; @@ -7,14 +7,12 @@ import { useContext } from 'react'; import { makeRenderDataClient } from '../../../test'; import { CacheProvider } from '../components'; import { StateContext } from '../context'; -import { useCache, useSuspense, useController } from '../hooks'; +import { useSuspense, useController, useQuery } from '../hooks'; import { payload, createPayload, users, nested, - paginatedFirstPage, - paginatedSecondPage, valuesFixture, } from '../test-fixtures'; @@ -83,10 +81,10 @@ describe('indexes', () => { const { fetch } = useController(); useSuspense(IndexedUserResource.getList); return { - bob: useCache(IndexedUserResource.getIndex, { + bob: useQuery(IndexedUser, { username: 'bob', }), - charlie: useCache(IndexedUserResource.getIndex, { + charlie: useQuery(IndexedUser, { username: 'charlie', }), fetch, diff --git a/packages/redux/README.md b/packages/redux/README.md index 03f470b15cde..2bc31ca815a5 100644 --- a/packages/redux/README.md +++ b/packages/redux/README.md @@ -74,7 +74,7 @@ return price.value; ### [Programmatic queries](https://dataclient.io/rest/api/Query) ```tsx -const sortedArticles = new Query( +const sortedArticles = new schema.Query( new schema.All(Article), (entries, { asc } = { asc: false }) => { const sorted = [...entries].sort((a, b) => a.title.localeCompare(b.title)); @@ -83,9 +83,9 @@ const sortedArticles = new Query( } ); -const articlesUnsorted = useCache(sortedArticles); -const articlesAscending = useCache(sortedArticles, { asc: true }); -const articlesDescending = useCache(sortedArticles, { asc: false }); +const articlesUnsorted = useQuery(sortedArticles); +const articlesAscending = useQuery(sortedArticles, { asc: true }); +const articlesDescending = useQuery(sortedArticles, { asc: false }); ``` ### ...all typed ...fast ...and consistent diff --git a/packages/rest/README.md b/packages/rest/README.md index 446eed2d4c04..5dc1e00a90b3 100644 --- a/packages/rest/README.md +++ b/packages/rest/README.md @@ -121,13 +121,13 @@ const deleteTodo = data => ctrl.fetch(TodoResource.delete, { id }); ### [Programmatic queries](https://dataclient.io/rest/api/Query) ```tsx -const queryRemainingTodos = new Query( +const queryRemainingTodos = new schema.Query( TodoResource.getList.schema, (entries) => entries.filter((todo) => !todo.completed).length, ); -const allRemainingTodos = useCache(queryRemainingTodos); -const firstUserRemainingTodos = useCache(queryRemainingTodos, { userId: 1 }); +const allRemainingTodos = useQuery(queryRemainingTodos); +const firstUserRemainingTodos = useQuery(queryRemainingTodos, { userId: 1 }); ``` ### TypeScript requirements diff --git a/website/sidebars-endpoint.json b/website/sidebars-endpoint.json index 55a4bb16191d..ef2e2f3fa6a4 100644 --- a/website/sidebars-endpoint.json +++ b/website/sidebars-endpoint.json @@ -3,10 +3,6 @@ "type": "doc", "id": "api/Endpoint" }, - { - "type": "doc", - "id": "api/Index" - }, { "type": "doc", "id": "api/schema" @@ -47,6 +43,10 @@ "type": "doc", "id": "api/All" }, + { + "type": "doc", + "id": "api/Query" + }, { "type": "doc", "id": "api/validateRequired" diff --git a/website/sidebars-rest.js b/website/sidebars-rest.js index 2c9edcecfd1c..d225d449fadb 100644 --- a/website/sidebars-rest.js +++ b/website/sidebars-rest.js @@ -79,14 +79,6 @@ module.exports = { type: 'doc', id: 'api/hookifyResource', }, - { - type: 'doc', - id: 'api/Query', - }, - { - type: 'doc', - id: 'api/Index', - }, ], }, { diff --git a/website/src/components/Playground/editor-types/@data-client/core.d.ts b/website/src/components/Playground/editor-types/@data-client/core.d.ts index bff9680f8c60..273345d09746 100644 --- a/website/src/components/Playground/editor-types/@data-client/core.d.ts +++ b/website/src/components/Playground/editor-types/@data-client/core.d.ts @@ -1,15 +1,18 @@ type Schema = null | string | { [K: string]: any; } | Schema[] | SchemaSimple | Serializable; +type Queryable = { + infer(args: readonly any[], indexes: NormalizedIndex, recurse: (...args: any) => any, entities: EntityTable): {}; +}; type Serializable = (value: any) => T; -interface SchemaSimple { - normalize(input: any, parent: any, key: any, visit: (...args: any) => any, addEntity: (...args: any) => any, visitedEntities: Record, storeEntities?: any, args?: any[]): any; - denormalize(input: {}, args: any, unvisit: (input: any, schema: any) => any): T; - infer(args: readonly any[], indexes: NormalizedIndex, recurse: (...args: any) => any, entities: EntityTable): any; +interface SchemaSimple { + normalize(input: any, parent: any, key: any, visit: (...args: any) => any, addEntity: (...args: any) => any, visitedEntities: Record, storeEntities: any, args: any[]): any; + denormalize(input: {}, args: readonly any[], unvisit: (input: any, schema: any) => any): T; + infer(args: Args, indexes: NormalizedIndex, recurse: (...args: any) => any, entities: EntityTable): any; } interface EntityInterface extends SchemaSimple { createIfValid(props: any): any; @@ -36,6 +39,11 @@ interface EntityTable { } | undefined; } +/** Attempts to infer reasonable input type to construct an Entity */ +type EntityFields = { + readonly [K in keyof U as U[K] extends (...args: any) => any ? never : K]?: U[K]; +}; + /** Maps entity dependencies to a value (usually their denormalized form) * * Dependencies store `Path` to enable quick traversal using only `State` @@ -86,16 +94,12 @@ interface NestedSchemaClass { interface RecordClass extends NestedSchemaClass { fromJS: (...args: any) => AbstractInstanceType; } -interface DenormalizeCache { - entities: { - [key: string]: { - [pk: string]: WeakMap>; - }; - }; - results: { - [key: string]: WeakEntityMap; +interface EntityCache { + [key: string]: { + [pk: string]: WeakMap>; }; } +type ResultCache = WeakEntityMap; type DenormalizeNullableNestedSchema = keyof S['schema'] extends never ? S['prototype'] : string extends keyof S['schema'] ? S['prototype'] : S['prototype']; type NormalizeReturnType = T extends (...args: any) => infer R ? R : never; type Denormalize = S extends EntityInterface ? U : S extends RecordClass ? AbstractInstanceType : S extends { @@ -118,6 +122,9 @@ type NormalizeNullable = S extends EntityInterface ? string | undefined : S e } ? NormalizeReturnType : S extends Serializable ? T : S extends Array ? Normalize[] | undefined : S extends { [K: string]: any; } ? NormalizedNullableObject : S; +type SchemaArgs = S extends EntityInterface ? [EntityFields] : S extends ({ + infer(args: infer Args, indexes: any, recurse: (...args: any) => any, entities: any): any; +}) ? Args : []; /** * Build the result parameter to denormalize from schema alone. @@ -365,6 +372,19 @@ interface State { readonly optimistic: (SetAction | OptimisticAction)[]; readonly lastReset: number; } +interface DenormalizeCache { + entities: EntityCache; + results: { + [key: string]: ResultCache; + }; + infer: ResultCache; + inputEndpointCache: { + [key: string]: unknown; + }; + queries: Map; +} interface Manager { getMiddleware(): Middleware$2; @@ -497,11 +517,33 @@ declare class Controller { * Gets the (globally referentially stable) response for a given endpoint/args pair from state given. * @see https://dataclient.io/docs/api/Controller#getResponse */ - getResponse: , "schema" | "key" | "invalidIfStale">, Args extends readonly [null] | readonly [...Parameters]>(endpoint: E, ...rest: [...Args, State]) => { - data: DenormalizeNullable; + getResponse(endpoint: E, ...rest: readonly [null, State]): { + data: DenormalizeNullable; + expiryStatus: ExpiryStatus; + expiresAt: number; + }; + getResponse(endpoint: E, ...rest: readonly [...Parameters, State]): { + data: DenormalizeNullable; expiryStatus: ExpiryStatus; expiresAt: number; }; + getResponse>(endpoint: E, ...rest: readonly [ + ...(readonly [...Parameters] | readonly [null]), + State + ]): { + data: DenormalizeNullable; + expiryStatus: ExpiryStatus; + expiresAt: number; + }; + /** + * Queries the store for a Querable schema + * @see https://dataclient.io/docs/api/Controller#query + */ + query(schema: S, ...rest: readonly [ + ...SchemaArgs, + Pick, 'entities' | 'entityMeta'> + ]): DenormalizeNullable | undefined; + private getSchemaResponse; } declare function createReducer(controller: Controller): ReducerType; @@ -983,4 +1025,4 @@ declare class DevToolsManager implements Manager { getMiddleware(): Middleware; } -export { AbstractInstanceType, ActionTypes, ConnectionListener, Controller, DataClientDispatch, DefaultConnectionListener, Denormalize, DenormalizeCache, DenormalizeNullable, DevToolsConfig, DevToolsManager, Dispatch$1 as Dispatch, EndpointExtraOptions, EndpointInterface, EndpointUpdateFunction, EntityInterface, ErrorTypes, ExpireAllAction, ExpiryStatus, FetchAction, FetchFunction, FetchMeta, GCAction, GenericDispatch, InvalidateAction, InvalidateAllAction, LogoutManager, Manager, Middleware$2 as Middleware, MiddlewareAPI$1 as MiddlewareAPI, NetworkError, NetworkManager, Normalize, NormalizeNullable, OptimisticAction, PK, PollingSubscription, ResetAction, ResetError, ResolveType, ResultEntry, Schema, SetAction, SetActionError, SetActionSuccess, SetMeta, SetTypes, State, SubscribeAction, SubscriptionManager, UnknownError, UnsubscribeAction, UpdateFunction, internal_d as __INTERNAL__, actionTypes_d as actionTypes, applyManager, createFetch, createReducer, createSet, initialState }; +export { AbstractInstanceType, ActionTypes, ConnectionListener, Controller, DataClientDispatch, DefaultConnectionListener, Denormalize, DenormalizeCache, DenormalizeNullable, DevToolsConfig, DevToolsManager, Dispatch$1 as Dispatch, EndpointExtraOptions, EndpointInterface, EndpointUpdateFunction, EntityCache, EntityInterface, ErrorTypes, ExpireAllAction, ExpiryStatus, FetchAction, FetchFunction, FetchMeta, GCAction, GenericDispatch, InvalidateAction, InvalidateAllAction, LogoutManager, Manager, Middleware$2 as Middleware, MiddlewareAPI$1 as MiddlewareAPI, NetworkError, NetworkManager, Normalize, NormalizeNullable, OptimisticAction, PK, PollingSubscription, Queryable, ResetAction, ResetError, ResolveType, ResultCache, ResultEntry, Schema, SchemaArgs, SetAction, SetActionError, SetActionSuccess, SetMeta, SetTypes, State, SubscribeAction, SubscriptionManager, UnknownError, UnsubscribeAction, UpdateFunction, internal_d as __INTERNAL__, actionTypes_d as actionTypes, applyManager, createFetch, createReducer, createSet, initialState }; diff --git a/website/src/components/Playground/editor-types/@data-client/endpoint.d.ts b/website/src/components/Playground/editor-types/@data-client/endpoint.d.ts index d3a2929294d0..f4e104e00127 100644 --- a/website/src/components/Playground/editor-types/@data-client/endpoint.d.ts +++ b/website/src/components/Playground/editor-types/@data-client/endpoint.d.ts @@ -8,6 +8,11 @@ interface UnknownError extends Error { } type ErrorTypes = NetworkError | UnknownError; +/** Attempts to infer reasonable input type to construct an Entity */ +type EntityFields = { + readonly [K in keyof U as U[K] extends (...args: any) => any ? never : K]?: U[K]; +}; + type AbstractInstanceType = T extends new (...args: any) => infer U ? U : T extends { prototype: infer U; } ? U : never; @@ -57,11 +62,9 @@ type NormalizeNullable = S extends EntityInterface ? string | undefined : S e interface EntityMap { readonly [k: string]: EntityInterface; } -type SchemaToArgs any, addEntity: (...args: any) => any, visitedEntities: Record, storeEntities: any, args: any): any; -}> = S extends ({ - normalize(input: any, parent: any, key: any, visit: (...args: any) => any, addEntity: (...args: any) => any, visitedEntities: Record, storeEntities: any, args: infer Args): any; -}) ? Args : never; +type SchemaArgs = S extends EntityInterface ? [EntityFields] : S extends ({ + infer(args: infer Args, indexes: any, recurse: (...args: any) => any, entities: any): any; +}) ? Args : []; interface SnapshotInterface { getResponse: , Args extends readonly [...Parameters]>(endpoint: E, ...args: Args) => { @@ -104,17 +107,20 @@ interface EndpointExtraOptions { type Schema = null | string | { [K: string]: any; } | Schema[] | SchemaSimple | Serializable; +type Queryable = { + infer(args: readonly any[], indexes: NormalizedIndex, recurse: (...args: any) => any, entities: EntityTable): {}; +}; type Serializable = (value: any) => T; -interface SchemaSimple { - normalize(input: any, parent: any, key: any, visit: (...args: any) => any, addEntity: (...args: any) => any, visitedEntities: Record, storeEntities: any, args: Args): any; +interface SchemaSimple { + normalize(input: any, parent: any, key: any, visit: (...args: any) => any, addEntity: (...args: any) => any, visitedEntities: Record, storeEntities: any, args: any[]): any; denormalize(input: {}, args: readonly any[], unvisit: (input: any, schema: any) => any): T; - infer(args: readonly any[], indexes: NormalizedIndex, recurse: (...args: any) => any, entities: EntityTable): any; + infer(args: Args, indexes: NormalizedIndex, recurse: (...args: any) => any, entities: EntityTable): any; } -interface SchemaClass extends SchemaSimple { +interface SchemaClass extends SchemaSimple { _normalizeNullable(): any; _denormalizeNullable(): N; } @@ -130,7 +136,7 @@ interface EntityInterface extends SchemaSimple { prototype: T; } /** Represents Array or Values */ -interface PolymorphicInterface extends SchemaSimple { +interface PolymorphicInterface extends SchemaSimple { readonly schema: any; _normalizeNullable(): any; _denormalizeNullable(): any; @@ -419,16 +425,28 @@ declare class Invalidate any): AbstractInstanceType; _denormalizeNullable(): AbstractInstanceType | undefined; _normalizeNullable(): string | undefined; } -type CollectionOptions, - body?: Record -], Parent = any> = ({ +/** + * Programmatic cache reading + * @see https://dataclient.io/rest/api/Query + */ +declare class Query, ...args: any) => any> implements SchemaSimple | undefined, ProcessParameters> { + schema: S; + process: P; + constructor(schema: S, process: P); + normalize(...args: any): any; + denormalize(input: {}, args: any, unvisit: any): ReturnType

| undefined; + infer(args: ProcessParameters, indexes: any, recurse: (schema: any, args: any, indexes: NormalizedIndex, entities: EntityTable) => any, entities: EntityTable): any; + _denormalizeNullable: (input: {}, args: readonly any[], unvisit: (input: any, schema: any) => any) => ReturnType

| undefined; +} +type ProcessParameters = P extends (entries: any, ...args: infer Par) => any ? Par extends [] ? SchemaArgs : Par & SchemaArgs : SchemaArgs; + +type CollectionOptions] | [urlParams: Record, body: any], Parent = any> = ({ nestKey?: (parent: Parent, key: string) => Record; } | { argsKey?: (...args: Args) => Record; @@ -448,7 +466,7 @@ interface CollectionInterface any, addEntity: (...args: any) => any, visitedEntities: Record, storeEntities: any, args: Args): string; + normalize(input: any, parent: Parent, key: string, visit: (...args: any) => any, addEntity: (...args: any) => any, visitedEntities: Record, storeEntities: any, args: any): string; merge(existing: any, incoming: any): any; shouldReorder(existingMeta: { date: number; @@ -477,7 +495,7 @@ interface CollectionInterface any | undefined; denormalize(input: any, args: readonly any[], unvisit: (input: any, schema: any) => any): ReturnType; _denormalizeNullable(): ReturnType; @@ -497,15 +515,9 @@ interface CollectionInterface; } ? Collection : never; } -type CollectionFromSchema, - body?: Record -], Parent = any> = CollectionInterface : S, Args, Parent>; +type CollectionFromSchema] | [urlParams: Record, body: any], Parent = any> = CollectionInterface : S, Args, Parent>; interface CollectionConstructor { - new , - body?: Record - ], Parent = any>(schema: S, options?: CollectionOptions): CollectionFromSchema; + new ] | [urlParams: Record, body: any], Parent = any>(schema: S, options?: CollectionOptions): CollectionFromSchema; readonly prototype: CollectionInterface; } type StrategyFunction = (value: any, parent: any, key: string) => T; @@ -540,7 +552,7 @@ declare class Array$1 implements SchemaClass { addEntity: (...args: any) => any, visitedEntities: Record, storeEntities: any, - args?: any[], + args: any[], ): (S extends EntityMap ? UnionResult : Normalize)[]; _normalizeNullable(): @@ -562,7 +574,7 @@ declare class Array$1 implements SchemaClass { indexes: NormalizedIndex, recurse: (...args: any) => any, entities: any, - ): any; + ): undefined; } /** @@ -599,9 +611,7 @@ declare class All< | (S extends EntityMap ? UnionResult : Normalize)[] | undefined; - _denormalizeNullable(): - | (S extends EntityMap ? T : Denormalize)[] - | undefined; + _denormalizeNullable(): (S extends EntityMap ? T : Denormalize)[]; denormalize( input: {}, @@ -610,7 +620,8 @@ declare class All< ): (S extends EntityMap ? T : Denormalize)[]; infer( - args: readonly any[], + // TODO: hack for now to allow for variable arg combinations with Query + args: [] | [unknown], indexes: NormalizedIndex, recurse: (...args: any) => any, entities: EntityTable, @@ -696,7 +707,7 @@ declare class Union implements SchemaClass { ): AbstractInstanceType; infer( - args: readonly any[], + args: [EntityFields>], indexes: NormalizedIndex, recurse: (...args: any) => any, entities: any, @@ -767,7 +778,7 @@ declare class Values implements SchemaClass { indexes: NormalizedIndex, recurse: (...args: any) => any, entities: any, - ): any; + ): undefined; } type CollectionArrayAdder = @@ -791,10 +802,10 @@ declare let CollectionRoot: CollectionConstructor; */ declare class Collection< S extends any[] | PolymorphicInterface = any, - Args extends any[] = [ - urlParams: Record, - body?: Record, - ], + Args extends any[] = + | [] + | [urlParams: Record] + | [urlParams: Record, body: any], Parent = any, > extends CollectionRoot {} @@ -825,7 +836,9 @@ type schema_d_Invalidate = Invalidate; declare const schema_d_Invalidate: typeof Invalidate; -type schema_d_SchemaClass = SchemaClass; +type schema_d_Query, ...args: any) => any> = Query; +declare const schema_d_Query: typeof Query; +type schema_d_SchemaClass = SchemaClass; type schema_d_All = All; declare const schema_d_All: typeof All; type schema_d_Union = Union; @@ -834,17 +847,14 @@ type schema_d_Values = Values; declare const schema_d_Values: typeof Values; type schema_d_CollectionArrayAdder = CollectionArrayAdder; declare const schema_d_CollectionRoot: typeof CollectionRoot; -type schema_d_Collection, - body?: Record, - ], Parent = any> = Collection; +type schema_d_Collection] + | [urlParams: Record, body: any], Parent = any> = Collection; declare const schema_d_Collection: typeof Collection; type schema_d_EntityInterface = EntityInterface; type schema_d_CollectionInterface = CollectionInterface; -type schema_d_CollectionFromSchema, - body?: Record -], Parent = any> = CollectionFromSchema; +type schema_d_CollectionFromSchema] | [urlParams: Record, body: any], Parent = any> = CollectionFromSchema; type schema_d_CollectionConstructor = CollectionConstructor; type schema_d_StrategyFunction = StrategyFunction; type schema_d_SchemaFunction = SchemaFunction; @@ -855,6 +865,7 @@ declare namespace schema_d { export { schema_d_EntityMap as EntityMap, schema_d_Invalidate as Invalidate, + schema_d_Query as Query, schema_d_SchemaClass as SchemaClass, Array$1 as Array, schema_d_All as All, @@ -932,39 +943,7 @@ declare function validateRequired(processedEntity: any, requiredDefaults: Record declare const INVALID: unique symbol; -/** - * Performant lookups by secondary indexes - * @see https://dataclient.io/docs/api/Index - */ -declare class Index>> { - schema: S; - constructor(schema: S, key?: (params: P) => string); - key(params?: P): string; -} -type ArrayElement = ArrayType[number]; -type IndexParams = S extends ({ - indexes: readonly string[]; -}) ? { - [K in Extract, keyof AbstractInstanceType>]?: AbstractInstanceType[K]; -} : Readonly; - -/** - * Programmatic cache reading - * @see https://dataclient.io/rest/api/Query - */ -declare class Query = SchemaToArgs, R = Denormalize> { - schema: QuerySchema; - process: (entries: Denormalize, ...args: P) => R; - constructor(schema: S, process?: (entries: Denormalize, ...args: P) => R); - key(...args: P): string; - protected createQuerySchema(schema: SchemaSimple): any; -} -type QuerySchema = Exclude & { - _denormalizeNullable(input: {}, args: readonly any[], unvisit: (input: any, schema: any) => any): R | undefined; - denormalize(input: {}, args: readonly any[], unvisit: (input: any, schema: any) => any): R; -}; - declare class AbortOptimistic extends Error { } -export { AbortOptimistic, AbstractInstanceType, Array$1 as Array, ArrayElement, Collection, Denormalize, DenormalizeNullable, Endpoint, EndpointExtendOptions, EndpointExtraOptions, EndpointInstance, EndpointInstanceInterface, EndpointInterface, EndpointOptions, EndpointParam, EndpointToFunction, Entity, ErrorTypes, ExpiryStatusInterface, ExtendableEndpoint, FetchFunction, INVALID, Index, IndexParams, Invalidate, KeyofEndpointInstance, MutateEndpoint, NetworkError, Normalize, NormalizeNullable, PolymorphicInterface, Query, ReadEndpoint, ResolveType, Schema, SchemaClass, SchemaSimple, SchemaToArgs, SnapshotInterface, UnknownError, schema_d as schema, validateRequired }; +export { AbortOptimistic, AbstractInstanceType, Array$1 as Array, Collection, Denormalize, DenormalizeNullable, Endpoint, EndpointExtendOptions, EndpointExtraOptions, EndpointInstance, EndpointInstanceInterface, EndpointInterface, EndpointOptions, EndpointParam, EndpointToFunction, Entity, ErrorTypes, ExpiryStatusInterface, ExtendableEndpoint, FetchFunction, INVALID, Invalidate, KeyofEndpointInstance, MutateEndpoint, NetworkError, Normalize, NormalizeNullable, PolymorphicInterface, ReadEndpoint, ResolveType, Schema, SchemaArgs, SchemaClass, SchemaSimple, SnapshotInterface, UnknownError, schema_d as schema, validateRequired }; diff --git a/website/src/components/Playground/editor-types/@data-client/graphql.d.ts b/website/src/components/Playground/editor-types/@data-client/graphql.d.ts index edff53e7e43a..73c5bc6b66c7 100644 --- a/website/src/components/Playground/editor-types/@data-client/graphql.d.ts +++ b/website/src/components/Playground/editor-types/@data-client/graphql.d.ts @@ -8,6 +8,11 @@ interface UnknownError extends Error { } type ErrorTypes = NetworkError | UnknownError; +/** Attempts to infer reasonable input type to construct an Entity */ +type EntityFields = { + readonly [K in keyof U as U[K] extends (...args: any) => any ? never : K]?: U[K]; +}; + type AbstractInstanceType = T extends new (...args: any) => infer U ? U : T extends { prototype: infer U; } ? U : never; @@ -57,11 +62,9 @@ type NormalizeNullable = S extends EntityInterface ? string | undefined : S e interface EntityMap { readonly [k: string]: EntityInterface; } -type SchemaToArgs any, addEntity: (...args: any) => any, visitedEntities: Record, storeEntities: any, args: any): any; -}> = S extends ({ - normalize(input: any, parent: any, key: any, visit: (...args: any) => any, addEntity: (...args: any) => any, visitedEntities: Record, storeEntities: any, args: infer Args): any; -}) ? Args : never; +type SchemaArgs = S extends EntityInterface ? [EntityFields] : S extends ({ + infer(args: infer Args, indexes: any, recurse: (...args: any) => any, entities: any): any; +}) ? Args : []; interface SnapshotInterface { getResponse: , Args extends readonly [...Parameters]>(endpoint: E, ...args: Args) => { @@ -104,17 +107,20 @@ interface EndpointExtraOptions { type Schema = null | string | { [K: string]: any; } | Schema[] | SchemaSimple | Serializable; +type Queryable = { + infer(args: readonly any[], indexes: NormalizedIndex, recurse: (...args: any) => any, entities: EntityTable): {}; +}; type Serializable = (value: any) => T; -interface SchemaSimple { - normalize(input: any, parent: any, key: any, visit: (...args: any) => any, addEntity: (...args: any) => any, visitedEntities: Record, storeEntities: any, args: Args): any; +interface SchemaSimple { + normalize(input: any, parent: any, key: any, visit: (...args: any) => any, addEntity: (...args: any) => any, visitedEntities: Record, storeEntities: any, args: any[]): any; denormalize(input: {}, args: readonly any[], unvisit: (input: any, schema: any) => any): T; - infer(args: readonly any[], indexes: NormalizedIndex, recurse: (...args: any) => any, entities: EntityTable): any; + infer(args: Args, indexes: NormalizedIndex, recurse: (...args: any) => any, entities: EntityTable): any; } -interface SchemaClass extends SchemaSimple { +interface SchemaClass extends SchemaSimple { _normalizeNullable(): any; _denormalizeNullable(): N; } @@ -130,7 +136,7 @@ interface EntityInterface extends SchemaSimple { prototype: T; } /** Represents Array or Values */ -interface PolymorphicInterface extends SchemaSimple { +interface PolymorphicInterface extends SchemaSimple { readonly schema: any; _normalizeNullable(): any; _denormalizeNullable(): any; @@ -419,16 +425,28 @@ declare class Invalidate any): AbstractInstanceType; _denormalizeNullable(): AbstractInstanceType | undefined; _normalizeNullable(): string | undefined; } -type CollectionOptions, - body?: Record -], Parent = any> = ({ +/** + * Programmatic cache reading + * @see https://dataclient.io/rest/api/Query + */ +declare class Query, ...args: any) => any> implements SchemaSimple | undefined, ProcessParameters> { + schema: S; + process: P; + constructor(schema: S, process: P); + normalize(...args: any): any; + denormalize(input: {}, args: any, unvisit: any): ReturnType

| undefined; + infer(args: ProcessParameters, indexes: any, recurse: (schema: any, args: any, indexes: NormalizedIndex, entities: EntityTable) => any, entities: EntityTable): any; + _denormalizeNullable: (input: {}, args: readonly any[], unvisit: (input: any, schema: any) => any) => ReturnType

| undefined; +} +type ProcessParameters = P extends (entries: any, ...args: infer Par) => any ? Par extends [] ? SchemaArgs : Par & SchemaArgs : SchemaArgs; + +type CollectionOptions] | [urlParams: Record, body: any], Parent = any> = ({ nestKey?: (parent: Parent, key: string) => Record; } | { argsKey?: (...args: Args) => Record; @@ -448,7 +466,7 @@ interface CollectionInterface any, addEntity: (...args: any) => any, visitedEntities: Record, storeEntities: any, args: Args): string; + normalize(input: any, parent: Parent, key: string, visit: (...args: any) => any, addEntity: (...args: any) => any, visitedEntities: Record, storeEntities: any, args: any): string; merge(existing: any, incoming: any): any; shouldReorder(existingMeta: { date: number; @@ -477,7 +495,7 @@ interface CollectionInterface any | undefined; denormalize(input: any, args: readonly any[], unvisit: (input: any, schema: any) => any): ReturnType; _denormalizeNullable(): ReturnType; @@ -497,15 +515,9 @@ interface CollectionInterface; } ? Collection : never; } -type CollectionFromSchema, - body?: Record -], Parent = any> = CollectionInterface : S, Args, Parent>; +type CollectionFromSchema] | [urlParams: Record, body: any], Parent = any> = CollectionInterface : S, Args, Parent>; interface CollectionConstructor { - new , - body?: Record - ], Parent = any>(schema: S, options?: CollectionOptions): CollectionFromSchema; + new ] | [urlParams: Record, body: any], Parent = any>(schema: S, options?: CollectionOptions): CollectionFromSchema; readonly prototype: CollectionInterface; } type StrategyFunction = (value: any, parent: any, key: string) => T; @@ -540,7 +552,7 @@ declare class Array$1 implements SchemaClass { addEntity: (...args: any) => any, visitedEntities: Record, storeEntities: any, - args?: any[], + args: any[], ): (S extends EntityMap ? UnionResult : Normalize)[]; _normalizeNullable(): @@ -562,7 +574,7 @@ declare class Array$1 implements SchemaClass { indexes: NormalizedIndex, recurse: (...args: any) => any, entities: any, - ): any; + ): undefined; } /** @@ -599,9 +611,7 @@ declare class All< | (S extends EntityMap ? UnionResult : Normalize)[] | undefined; - _denormalizeNullable(): - | (S extends EntityMap ? T : Denormalize)[] - | undefined; + _denormalizeNullable(): (S extends EntityMap ? T : Denormalize)[]; denormalize( input: {}, @@ -610,7 +620,8 @@ declare class All< ): (S extends EntityMap ? T : Denormalize)[]; infer( - args: readonly any[], + // TODO: hack for now to allow for variable arg combinations with Query + args: [] | [unknown], indexes: NormalizedIndex, recurse: (...args: any) => any, entities: EntityTable, @@ -696,7 +707,7 @@ declare class Union implements SchemaClass { ): AbstractInstanceType; infer( - args: readonly any[], + args: [EntityFields>], indexes: NormalizedIndex, recurse: (...args: any) => any, entities: any, @@ -767,7 +778,7 @@ declare class Values implements SchemaClass { indexes: NormalizedIndex, recurse: (...args: any) => any, entities: any, - ): any; + ): undefined; } type CollectionArrayAdder = @@ -791,10 +802,10 @@ declare let CollectionRoot: CollectionConstructor; */ declare class Collection< S extends any[] | PolymorphicInterface = any, - Args extends any[] = [ - urlParams: Record, - body?: Record, - ], + Args extends any[] = + | [] + | [urlParams: Record] + | [urlParams: Record, body: any], Parent = any, > extends CollectionRoot {} @@ -825,7 +836,9 @@ type schema_d_Invalidate = Invalidate; declare const schema_d_Invalidate: typeof Invalidate; -type schema_d_SchemaClass = SchemaClass; +type schema_d_Query, ...args: any) => any> = Query; +declare const schema_d_Query: typeof Query; +type schema_d_SchemaClass = SchemaClass; type schema_d_All = All; declare const schema_d_All: typeof All; type schema_d_Union = Union; @@ -834,17 +847,14 @@ type schema_d_Values = Values; declare const schema_d_Values: typeof Values; type schema_d_CollectionArrayAdder = CollectionArrayAdder; declare const schema_d_CollectionRoot: typeof CollectionRoot; -type schema_d_Collection, - body?: Record, - ], Parent = any> = Collection; +type schema_d_Collection] + | [urlParams: Record, body: any], Parent = any> = Collection; declare const schema_d_Collection: typeof Collection; type schema_d_EntityInterface = EntityInterface; type schema_d_CollectionInterface = CollectionInterface; -type schema_d_CollectionFromSchema, - body?: Record -], Parent = any> = CollectionFromSchema; +type schema_d_CollectionFromSchema] | [urlParams: Record, body: any], Parent = any> = CollectionFromSchema; type schema_d_CollectionConstructor = CollectionConstructor; type schema_d_StrategyFunction = StrategyFunction; type schema_d_SchemaFunction = SchemaFunction; @@ -855,6 +865,7 @@ declare namespace schema_d { export { schema_d_EntityMap as EntityMap, schema_d_Invalidate as Invalidate, + schema_d_Query as Query, schema_d_SchemaClass as SchemaClass, Array$1 as Array, schema_d_All as All, @@ -932,38 +943,6 @@ declare function validateRequired(processedEntity: any, requiredDefaults: Record declare const INVALID: unique symbol; -/** - * Performant lookups by secondary indexes - * @see https://dataclient.io/docs/api/Index - */ -declare class Index>> { - schema: S; - constructor(schema: S, key?: (params: P) => string); - key(params?: P): string; -} -type ArrayElement = ArrayType[number]; -type IndexParams = S extends ({ - indexes: readonly string[]; -}) ? { - [K in Extract, keyof AbstractInstanceType>]?: AbstractInstanceType[K]; -} : Readonly; - -/** - * Programmatic cache reading - * @see https://dataclient.io/rest/api/Query - */ -declare class Query = SchemaToArgs, R = Denormalize> { - schema: QuerySchema; - process: (entries: Denormalize, ...args: P) => R; - constructor(schema: S, process?: (entries: Denormalize, ...args: P) => R); - key(...args: P): string; - protected createQuerySchema(schema: SchemaSimple): any; -} -type QuerySchema = Exclude & { - _denormalizeNullable(input: {}, args: readonly any[], unvisit: (input: any, schema: any) => any): R | undefined; - denormalize(input: {}, args: readonly any[], unvisit: (input: any, schema: any) => any): R; -}; - declare class AbortOptimistic extends Error { } @@ -1009,4 +988,4 @@ interface GQLError { path: (string | number)[]; } -export { AbortOptimistic, AbstractInstanceType, Array$1 as Array, ArrayElement, Collection, Denormalize, DenormalizeNullable, Endpoint, EndpointExtendOptions, EndpointExtraOptions, EndpointInstance, EndpointInstanceInterface, EndpointInterface, EndpointOptions, EndpointParam, EndpointToFunction, Entity, ErrorTypes, ExpiryStatusInterface, ExtendableEndpoint, FetchFunction, GQLEndpoint, GQLEntity, GQLError, GQLNetworkError, GQLOptions, INVALID, Index, IndexParams, Invalidate, KeyofEndpointInstance, MutateEndpoint, NetworkError, Normalize, NormalizeNullable, PolymorphicInterface, Query, ReadEndpoint, ResolveType, Schema, SchemaClass, SchemaSimple, SchemaToArgs, SnapshotInterface, UnknownError, schema_d as schema, validateRequired }; +export { AbortOptimistic, AbstractInstanceType, Array$1 as Array, Collection, Denormalize, DenormalizeNullable, Endpoint, EndpointExtendOptions, EndpointExtraOptions, EndpointInstance, EndpointInstanceInterface, EndpointInterface, EndpointOptions, EndpointParam, EndpointToFunction, Entity, ErrorTypes, ExpiryStatusInterface, ExtendableEndpoint, FetchFunction, GQLEndpoint, GQLEntity, GQLError, GQLNetworkError, GQLOptions, INVALID, Invalidate, KeyofEndpointInstance, MutateEndpoint, NetworkError, Normalize, NormalizeNullable, PolymorphicInterface, ReadEndpoint, ResolveType, Schema, SchemaArgs, SchemaClass, SchemaSimple, SnapshotInterface, UnknownError, schema_d as schema, validateRequired }; diff --git a/website/src/components/Playground/editor-types/@data-client/normalizr.d.ts b/website/src/components/Playground/editor-types/@data-client/normalizr.d.ts index 3f6c322f2026..1c147dbed0ba 100644 --- a/website/src/components/Playground/editor-types/@data-client/normalizr.d.ts +++ b/website/src/components/Playground/editor-types/@data-client/normalizr.d.ts @@ -1,17 +1,20 @@ type Schema = null | string | { [K: string]: any; } | Schema[] | SchemaSimple | Serializable; +type Queryable = { + infer(args: readonly any[], indexes: NormalizedIndex, recurse: (...args: any) => any, entities: EntityTable): {}; +}; type Serializable = (value: any) => T; -interface SchemaSimple { - normalize(input: any, parent: any, key: any, visit: (...args: any) => any, addEntity: (...args: any) => any, visitedEntities: Record, storeEntities?: any, args?: any[]): any; - denormalize(input: {}, args: any, unvisit: (input: any, schema: any) => any): T; - infer(args: readonly any[], indexes: NormalizedIndex, recurse: (...args: any) => any, entities: EntityTable): any; +interface SchemaSimple { + normalize(input: any, parent: any, key: any, visit: (...args: any) => any, addEntity: (...args: any) => any, visitedEntities: Record, storeEntities: any, args: any[]): any; + denormalize(input: {}, args: readonly any[], unvisit: (input: any, schema: any) => any): T; + infer(args: Args, indexes: NormalizedIndex, recurse: (...args: any) => any, entities: EntityTable): any; } -interface SchemaClass extends SchemaSimple { +interface SchemaClass extends SchemaSimple { _normalizeNullable(): any; _denormalizeNullable(): N; } @@ -40,6 +43,11 @@ interface EntityTable { } | undefined; } +/** Attempts to infer reasonable input type to construct an Entity */ +type EntityFields = { + readonly [K in keyof U as U[K] extends (...args: any) => any ? never : K]?: U[K]; +}; + /** Maps entity dependencies to a value (usually their denormalized form) * * Dependencies store `Path` to enable quick traversal using only `State` @@ -90,16 +98,12 @@ interface NestedSchemaClass { interface RecordClass extends NestedSchemaClass { fromJS: (...args: any) => AbstractInstanceType; } -interface DenormalizeCache { - entities: { - [key: string]: { - [pk: string]: WeakMap>; - }; - }; - results: { - [key: string]: WeakEntityMap; +interface EntityCache { + [key: string]: { + [pk: string]: WeakMap>; }; } +type ResultCache = WeakEntityMap; type DenormalizeNullableNestedSchema = keyof S['schema'] extends never ? S['prototype'] : string extends keyof S['schema'] ? S['prototype'] : S['prototype']; type NormalizeReturnType = T extends (...args: any) => infer R ? R : never; type Denormalize = S extends EntityInterface ? U : S extends RecordClass ? AbstractInstanceType : S extends { @@ -136,10 +140,13 @@ type NormalizedSchema = { }; }; }; +type SchemaArgs = S extends EntityInterface ? [EntityFields] : S extends ({ + infer(args: infer Args, indexes: any, recurse: (...args: any) => any, entities: any): any; +}) ? Args : []; declare function denormalize$1(input: any, schema: S | undefined, entities: any, args?: readonly any[]): DenormalizeNullable | symbol; -declare function denormalize(input: unknown, schema: S | undefined, entities: any, entityCache?: DenormalizeCache['entities'], resultCache?: DenormalizeCache['results'][string], args?: readonly any[]): { +declare function denormalize(input: unknown, schema: S | undefined, entities: any, entityCache?: EntityCache, resultCache?: ResultCache, args?: readonly any[]): { data: DenormalizeNullable | symbol; paths: Path[]; }; @@ -250,4 +257,4 @@ type FetchFunction = (...args: A) => Pr declare const INVALID: unique symbol; -export { AbstractInstanceType, ArrayElement, Denormalize, DenormalizeCache, DenormalizeNullable, EndpointExtraOptions, EndpointInterface, EntityInterface, EntityTable, ErrorTypes, ExpiryStatus, ExpiryStatusInterface, FetchFunction, INVALID, IndexInterface, IndexParams, InferReturn, MutateEndpoint, NetworkError, Normalize, NormalizeNullable, NormalizeReturnType, NormalizedIndex, NormalizedSchema, OptimisticUpdateParams, Path, ReadEndpoint, ResolveType, Schema, SchemaClass, SchemaSimple, Serializable, SnapshotInterface, UnknownError, UpdateFunction, WeakEntityMap, denormalize$1 as denormalize, denormalize as denormalizeCached, inferResults, isEntity, normalize, validateInference }; +export { AbstractInstanceType, ArrayElement, Denormalize, DenormalizeNullable, EndpointExtraOptions, EndpointInterface, EntityCache, EntityInterface, EntityTable, ErrorTypes, ExpiryStatus, ExpiryStatusInterface, FetchFunction, INVALID, IndexInterface, IndexParams, InferReturn, MutateEndpoint, NetworkError, Normalize, NormalizeNullable, NormalizeReturnType, NormalizedIndex, NormalizedSchema, OptimisticUpdateParams, Path, Queryable, ReadEndpoint, ResolveType, ResultCache, Schema, SchemaArgs, SchemaClass, SchemaSimple, Serializable, SnapshotInterface, UnknownError, UpdateFunction, WeakEntityMap, denormalize$1 as denormalize, denormalize as denormalizeCached, inferResults, isEntity, normalize, validateInference }; diff --git a/website/src/components/Playground/editor-types/@data-client/react.d.ts b/website/src/components/Playground/editor-types/@data-client/react.d.ts index eec864983540..7e0a98c445fa 100644 --- a/website/src/components/Playground/editor-types/@data-client/react.d.ts +++ b/website/src/components/Playground/editor-types/@data-client/react.d.ts @@ -1,5 +1,5 @@ import * as _data_client_core from '@data-client/core'; -import { Manager, State as State$1, Controller, NetworkError, EndpointInterface, FetchFunction, Schema, DenormalizeNullable, ResolveType, Denormalize, UnknownError, ErrorTypes as ErrorTypes$1, ActionTypes, __INTERNAL__, createReducer, applyManager } from '@data-client/core'; +import { Manager, State as State$1, Controller, NetworkError, EndpointInterface, FetchFunction, Schema, DenormalizeNullable, ResolveType, Denormalize, Queryable, SchemaArgs, UnknownError, ErrorTypes as ErrorTypes$1, ActionTypes, __INTERNAL__, createReducer, applyManager } from '@data-client/core'; export { AbstractInstanceType, ActionTypes, Controller, DataClientDispatch, DefaultConnectionListener, Denormalize, DenormalizeNullable, DevToolsManager, Dispatch, EndpointExtraOptions, EndpointInterface, ErrorTypes, ExpiryStatus, FetchAction, FetchFunction, GenericDispatch, InvalidateAction, LogoutManager, Manager, Middleware, MiddlewareAPI, NetworkError, NetworkManager, Normalize, NormalizeNullable, PK, PollingSubscription, ResetAction, ResolveType, Schema, SetAction, SetTypes, State, SubscribeAction, SubscriptionManager, UnknownError, UnsubscribeAction, UpdateFunction, actionTypes } from '@data-client/core'; import * as react_jsx_runtime from 'react/jsx-runtime'; import React$1, { Context } from 'react'; @@ -113,6 +113,14 @@ declare function useSuspense, 'key' | 'schema' | 'invalidIfStale'>, Args extends readonly [...Parameters] | readonly [null]>(endpoint: E, ...args: Args): E['schema'] extends undefined | null ? E extends (...args: any) => any ? ResolveType | undefined : any : DenormalizeNullable; +/** + * Query the store. + * + * `useQuery` results are globally memoized. + * @see https://dataclient.io/docs/api/useQuery + */ +declare function useQuery(schema: S, ...args: SchemaArgs): DenormalizeNullable | undefined; + type ErrorTypes = NetworkError | UnknownError; type UseErrorReturn

= P extends [null] ? undefined : ErrorTypes | undefined; /** @@ -214,4 +222,4 @@ declare namespace internal_d { /** Turns a dispatch function into one that resolves once its been commited */ declare function usePromisifiedDispatch>(dispatch: React$1.Dispatch>, state: React$1.ReducerState): (action: React$1.ReducerAction) => Promise; -export { _default as AsyncBoundary, Loading as BackupLoading, CacheProvider, ControllerContext, DevToolsPosition, NetworkErrorBoundary, ProviderProps, StateContext, Store, StoreContext, UniversalSuspense, internal_d as __INTERNAL__, getDefaultManagers, useCache, useController, useDLE, useError, useFetch, useLive, usePromisifiedDispatch, useSubscription, useSuspense }; +export { _default as AsyncBoundary, Loading as BackupLoading, CacheProvider, ControllerContext, DevToolsPosition, NetworkErrorBoundary, ProviderProps, StateContext, Store, StoreContext, UniversalSuspense, internal_d as __INTERNAL__, getDefaultManagers, useCache, useController, useDLE, useError, useFetch, useLive, usePromisifiedDispatch, useQuery, useSubscription, useSuspense }; diff --git a/website/src/components/Playground/editor-types/@data-client/rest.d.ts b/website/src/components/Playground/editor-types/@data-client/rest.d.ts index 8de065cc288b..409367107eda 100644 --- a/website/src/components/Playground/editor-types/@data-client/rest.d.ts +++ b/website/src/components/Playground/editor-types/@data-client/rest.d.ts @@ -10,6 +10,11 @@ interface UnknownError extends Error { } type ErrorTypes = NetworkError$1 | UnknownError; +/** Attempts to infer reasonable input type to construct an Entity */ +type EntityFields = { + readonly [K in keyof U as U[K] extends (...args: any) => any ? never : K]?: U[K]; +}; + type AbstractInstanceType = T extends new (...args: any) => infer U ? U : T extends { prototype: infer U; } ? U : never; @@ -59,11 +64,9 @@ type NormalizeNullable = S extends EntityInterface ? string | undefined : S e interface EntityMap { readonly [k: string]: EntityInterface; } -type SchemaToArgs any, addEntity: (...args: any) => any, visitedEntities: Record, storeEntities: any, args: any): any; -}> = S extends ({ - normalize(input: any, parent: any, key: any, visit: (...args: any) => any, addEntity: (...args: any) => any, visitedEntities: Record, storeEntities: any, args: infer Args): any; -}) ? Args : never; +type SchemaArgs = S extends EntityInterface ? [EntityFields] : S extends ({ + infer(args: infer Args, indexes: any, recurse: (...args: any) => any, entities: any): any; +}) ? Args : []; interface SnapshotInterface { getResponse: , Args extends readonly [...Parameters]>(endpoint: E, ...args: Args) => { @@ -106,17 +109,20 @@ interface EndpointExtraOptions { type Schema = null | string | { [K: string]: any; } | Schema[] | SchemaSimple | Serializable; +type Queryable = { + infer(args: readonly any[], indexes: NormalizedIndex, recurse: (...args: any) => any, entities: EntityTable): {}; +}; type Serializable = (value: any) => T; -interface SchemaSimple { - normalize(input: any, parent: any, key: any, visit: (...args: any) => any, addEntity: (...args: any) => any, visitedEntities: Record, storeEntities: any, args: Args): any; +interface SchemaSimple { + normalize(input: any, parent: any, key: any, visit: (...args: any) => any, addEntity: (...args: any) => any, visitedEntities: Record, storeEntities: any, args: any[]): any; denormalize(input: {}, args: readonly any[], unvisit: (input: any, schema: any) => any): T; - infer(args: readonly any[], indexes: NormalizedIndex, recurse: (...args: any) => any, entities: EntityTable): any; + infer(args: Args, indexes: NormalizedIndex, recurse: (...args: any) => any, entities: EntityTable): any; } -interface SchemaClass extends SchemaSimple { +interface SchemaClass extends SchemaSimple { _normalizeNullable(): any; _denormalizeNullable(): N; } @@ -132,7 +138,7 @@ interface EntityInterface extends SchemaSimple { prototype: T; } /** Represents Array or Values */ -interface PolymorphicInterface extends SchemaSimple { +interface PolymorphicInterface extends SchemaSimple { readonly schema: any; _normalizeNullable(): any; _denormalizeNullable(): any; @@ -417,16 +423,28 @@ declare class Invalidate any): AbstractInstanceType; _denormalizeNullable(): AbstractInstanceType | undefined; _normalizeNullable(): string | undefined; } -type CollectionOptions, - body?: Record -], Parent = any> = ({ +/** + * Programmatic cache reading + * @see https://dataclient.io/rest/api/Query + */ +declare class Query, ...args: any) => any> implements SchemaSimple | undefined, ProcessParameters> { + schema: S; + process: P; + constructor(schema: S, process: P); + normalize(...args: any): any; + denormalize(input: {}, args: any, unvisit: any): ReturnType

| undefined; + infer(args: ProcessParameters, indexes: any, recurse: (schema: any, args: any, indexes: NormalizedIndex, entities: EntityTable) => any, entities: EntityTable): any; + _denormalizeNullable: (input: {}, args: readonly any[], unvisit: (input: any, schema: any) => any) => ReturnType

| undefined; +} +type ProcessParameters = P extends (entries: any, ...args: infer Par) => any ? Par extends [] ? SchemaArgs : Par & SchemaArgs : SchemaArgs; + +type CollectionOptions] | [urlParams: Record, body: any], Parent = any> = ({ nestKey?: (parent: Parent, key: string) => Record; } | { argsKey?: (...args: Args) => Record; @@ -446,7 +464,7 @@ interface CollectionInterface any, addEntity: (...args: any) => any, visitedEntities: Record, storeEntities: any, args: Args): string; + normalize(input: any, parent: Parent, key: string, visit: (...args: any) => any, addEntity: (...args: any) => any, visitedEntities: Record, storeEntities: any, args: any): string; merge(existing: any, incoming: any): any; shouldReorder(existingMeta: { date: number; @@ -475,7 +493,7 @@ interface CollectionInterface any | undefined; denormalize(input: any, args: readonly any[], unvisit: (input: any, schema: any) => any): ReturnType; _denormalizeNullable(): ReturnType; @@ -495,15 +513,9 @@ interface CollectionInterface; } ? Collection : never; } -type CollectionFromSchema, - body?: Record -], Parent = any> = CollectionInterface : S, Args, Parent>; +type CollectionFromSchema] | [urlParams: Record, body: any], Parent = any> = CollectionInterface : S, Args, Parent>; interface CollectionConstructor { - new , - body?: Record - ], Parent = any>(schema: S, options?: CollectionOptions): CollectionFromSchema; + new ] | [urlParams: Record, body: any], Parent = any>(schema: S, options?: CollectionOptions): CollectionFromSchema; readonly prototype: CollectionInterface; } type StrategyFunction = (value: any, parent: any, key: string) => T; @@ -538,7 +550,7 @@ declare class Array$1 implements SchemaClass { addEntity: (...args: any) => any, visitedEntities: Record, storeEntities: any, - args?: any[], + args: any[], ): (S extends EntityMap ? UnionResult : Normalize)[]; _normalizeNullable(): @@ -560,7 +572,7 @@ declare class Array$1 implements SchemaClass { indexes: NormalizedIndex, recurse: (...args: any) => any, entities: any, - ): any; + ): undefined; } /** @@ -597,9 +609,7 @@ declare class All< | (S extends EntityMap ? UnionResult : Normalize)[] | undefined; - _denormalizeNullable(): - | (S extends EntityMap ? T : Denormalize)[] - | undefined; + _denormalizeNullable(): (S extends EntityMap ? T : Denormalize)[]; denormalize( input: {}, @@ -608,7 +618,8 @@ declare class All< ): (S extends EntityMap ? T : Denormalize)[]; infer( - args: readonly any[], + // TODO: hack for now to allow for variable arg combinations with Query + args: [] | [unknown], indexes: NormalizedIndex, recurse: (...args: any) => any, entities: EntityTable, @@ -694,7 +705,7 @@ declare class Union implements SchemaClass { ): AbstractInstanceType; infer( - args: readonly any[], + args: [EntityFields>], indexes: NormalizedIndex, recurse: (...args: any) => any, entities: any, @@ -765,7 +776,7 @@ declare class Values implements SchemaClass { indexes: NormalizedIndex, recurse: (...args: any) => any, entities: any, - ): any; + ): undefined; } type CollectionArrayAdder = @@ -789,10 +800,10 @@ declare let CollectionRoot: CollectionConstructor; */ declare class Collection< S extends any[] | PolymorphicInterface = any, - Args extends any[] = [ - urlParams: Record, - body?: Record, - ], + Args extends any[] = + | [] + | [urlParams: Record] + | [urlParams: Record, body: any], Parent = any, > extends CollectionRoot {} @@ -823,7 +834,9 @@ type schema_d_Invalidate = Invalidate; declare const schema_d_Invalidate: typeof Invalidate; -type schema_d_SchemaClass = SchemaClass; +type schema_d_Query, ...args: any) => any> = Query; +declare const schema_d_Query: typeof Query; +type schema_d_SchemaClass = SchemaClass; type schema_d_All = All; declare const schema_d_All: typeof All; type schema_d_Union = Union; @@ -832,17 +845,14 @@ type schema_d_Values = Values; declare const schema_d_Values: typeof Values; type schema_d_CollectionArrayAdder = CollectionArrayAdder; declare const schema_d_CollectionRoot: typeof CollectionRoot; -type schema_d_Collection, - body?: Record, - ], Parent = any> = Collection; +type schema_d_Collection] + | [urlParams: Record, body: any], Parent = any> = Collection; declare const schema_d_Collection: typeof Collection; type schema_d_EntityInterface = EntityInterface; type schema_d_CollectionInterface = CollectionInterface; -type schema_d_CollectionFromSchema, - body?: Record -], Parent = any> = CollectionFromSchema; +type schema_d_CollectionFromSchema] | [urlParams: Record, body: any], Parent = any> = CollectionFromSchema; type schema_d_CollectionConstructor = CollectionConstructor; type schema_d_StrategyFunction = StrategyFunction; type schema_d_SchemaFunction = SchemaFunction; @@ -853,6 +863,7 @@ declare namespace schema_d { export { schema_d_EntityMap as EntityMap, schema_d_Invalidate as Invalidate, + schema_d_Query as Query, schema_d_SchemaClass as SchemaClass, Array$1 as Array, schema_d_All as All, @@ -930,38 +941,6 @@ declare function validateRequired(processedEntity: any, requiredDefaults: Record declare const INVALID: unique symbol; -/** - * Performant lookups by secondary indexes - * @see https://dataclient.io/docs/api/Index - */ -declare class Index>> { - schema: S; - constructor(schema: S, key?: (params: P) => string); - key(params?: P): string; -} -type ArrayElement = ArrayType[number]; -type IndexParams = S extends ({ - indexes: readonly string[]; -}) ? { - [K in Extract, keyof AbstractInstanceType>]?: AbstractInstanceType[K]; -} : Readonly; - -/** - * Programmatic cache reading - * @see https://dataclient.io/rest/api/Query - */ -declare class Query = SchemaToArgs, R = Denormalize> { - schema: QuerySchema; - process: (entries: Denormalize, ...args: P) => R; - constructor(schema: S, process?: (entries: Denormalize, ...args: P) => R); - key(...args: P): string; - protected createQuerySchema(schema: SchemaSimple): any; -} -type QuerySchema = Exclude & { - _denormalizeNullable(input: {}, args: readonly any[], unvisit: (input: any, schema: any) => any): R | undefined; - denormalize(input: {}, args: readonly any[], unvisit: (input: any, schema: any) => any): R; -}; - declare class AbortOptimistic extends Error { } @@ -1539,4 +1518,4 @@ declare class NetworkError extends Error { constructor(response: Response); } -export { AbortOptimistic, AbstractInstanceType, AddEndpoint, Array$1 as Array, ArrayElement, Collection, CustomResource, Defaults, Denormalize, DenormalizeNullable, Endpoint, EndpointExtendOptions, EndpointExtraOptions, EndpointInstance, EndpointInstanceInterface, EndpointInterface, EndpointOptions, EndpointParam, EndpointToFunction, Entity, ErrorTypes, ExpiryStatusInterface, ExtendableEndpoint, ExtendedResource, FetchFunction, FetchGet, FetchMutate, FromFallBack, GetEndpoint, HookResource, HookableEndpointInterface, INVALID, Index, IndexParams, Invalidate, KeyofEndpointInstance, KeyofRestEndpoint, KeysToArgs, MethodToSide, MutateEndpoint, NetworkError, Normalize, NormalizeNullable, OptionsToFunction, PaginationEndpoint, PaginationFieldEndpoint, ParamFetchNoBody, ParamFetchWithBody, PartialRestGenerics, PathArgs, PathArgsAndSearch, PathKeys, PolymorphicInterface, Query, ReadEndpoint, ResolveType, Resource, ResourceEndpointExtensions, ResourceExtension, ResourceGenerics, ResourceOptions, RestEndpoint, RestEndpointConstructor, RestEndpointConstructorOptions, RestEndpointExtendOptions, RestEndpointOptions, RestExtendedEndpoint, RestFetch, RestGenerics, RestInstance, RestInstanceBase, RestType, RestTypeNoBody, RestTypeWithBody, Schema, SchemaClass, SchemaSimple, SchemaToArgs, ShortenPath, SnapshotInterface, UnknownError, createResource, getUrlBase, getUrlTokens, hookifyResource, schema_d as schema, validateRequired }; +export { AbortOptimistic, AbstractInstanceType, AddEndpoint, Array$1 as Array, Collection, CustomResource, Defaults, Denormalize, DenormalizeNullable, Endpoint, EndpointExtendOptions, EndpointExtraOptions, EndpointInstance, EndpointInstanceInterface, EndpointInterface, EndpointOptions, EndpointParam, EndpointToFunction, Entity, ErrorTypes, ExpiryStatusInterface, ExtendableEndpoint, ExtendedResource, FetchFunction, FetchGet, FetchMutate, FromFallBack, GetEndpoint, HookResource, HookableEndpointInterface, INVALID, Invalidate, KeyofEndpointInstance, KeyofRestEndpoint, KeysToArgs, MethodToSide, MutateEndpoint, NetworkError, Normalize, NormalizeNullable, OptionsToFunction, PaginationEndpoint, PaginationFieldEndpoint, ParamFetchNoBody, ParamFetchWithBody, PartialRestGenerics, PathArgs, PathArgsAndSearch, PathKeys, PolymorphicInterface, ReadEndpoint, ResolveType, Resource, ResourceEndpointExtensions, ResourceExtension, ResourceGenerics, ResourceOptions, RestEndpoint, RestEndpointConstructor, RestEndpointConstructorOptions, RestEndpointExtendOptions, RestEndpointOptions, RestExtendedEndpoint, RestFetch, RestGenerics, RestInstance, RestInstanceBase, RestType, RestTypeNoBody, RestTypeWithBody, Schema, SchemaArgs, SchemaClass, SchemaSimple, ShortenPath, SnapshotInterface, UnknownError, createResource, getUrlBase, getUrlTokens, hookifyResource, schema_d as schema, validateRequired }; diff --git a/website/src/components/Playground/editor-types/react.d.ts b/website/src/components/Playground/editor-types/react.d.ts index 66da98fd09f3..44f92a1fc9e5 100755 --- a/website/src/components/Playground/editor-types/react.d.ts +++ b/website/src/components/Playground/editor-types/react.d.ts @@ -20,11 +20,26 @@ type NativePointerEvent = PointerEvent; type NativeTransitionEvent = TransitionEvent; type NativeUIEvent = UIEvent; type NativeWheelEvent = WheelEvent; + +/** + * Used to represent DOM API's where users can either pass + * true or false as a boolean or as its equivalent strings. + */ type Booleanish = boolean | "true" | "false"; + +/** + * @see {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/crossorigin MDN} + */ type CrossOrigin = "anonymous" | "use-credentials" | "" | undefined; declare const UNDEFINED_VOID_ONLY: unique symbol; -// Destructors are only allowed to return void. + +/** + * The function returned from an effect passed to {@link React.useEffect useEffect}, + * which can be used to clean up the effect when the component unmounts. + * + * @see {@link https://react.dev/reference/react/useEffect React Docs} + */ type Destructor = () => void | { [UNDEFINED_VOID_ONLY]: never }; type VoidOrUndefinedOnly = void | { [UNDEFINED_VOID_ONLY]: never }; @@ -37,33 +52,128 @@ declare namespace React { // React Elements // ---------------------------------------------------------------------- + /** + * Used to retrieve the possible components which accept a given set of props. + * + * Can be passed no type parameters to get a union of all possible components + * and tags. + * + * Is a superset of {@link ComponentType}. + * + * @template P The props to match against. If not passed, defaults to any. + * @template Tag An optional tag to match against. If not passed, attempts to match against all possible tags. + * + * @example + * + * ```tsx + * // All components and tags (img, embed etc.) + * // which accept `src` + * type SrcComponents = ElementType<{ src: any }>; + * ``` + * + * @example + * + * ```tsx + * // All components + * type AllComponents = ElementType; + * ``` + * + * @example + * + * ```tsx + * // All custom components which match `src`, and tags which + * // match `src`, narrowed down to just `audio` and `embed` + * type SrcComponents = ElementType<{ src: any }, 'audio' | 'embed'>; + * ``` + */ type ElementType

= | { [K in Tag]: P extends JSX.IntrinsicElements[K] ? K : never }[Tag] | ComponentType

; + + /** + * Represents any user-defined component, either as a function component or + * a class component. + * + * @template P The props the component accepts. + * + * @see {@link ComponentClass} + * @see {@link FunctionComponent} + */ type ComponentType

= ComponentClass

| FunctionComponent

; type JSXElementConstructor

= | (( props: P, /** - * @deprecated https://legacy.reactjs.org/docs/legacy-context.html#referencing-context-in-stateless-function-components + * @deprecated + * + * @see {@link https://legacy.reactjs.org/docs/legacy-context.html#referencing-context-in-stateless-function-components React Docs} */ deprecatedLegacyContext?: any, ) => ReactNode) | (new( props: P, /** - * @deprecated https://legacy.reactjs.org/docs/legacy-context.html#referencing-context-in-lifecycle-methods + * @deprecated + * + * @see {@link https://legacy.reactjs.org/docs/legacy-context.html#referencing-context-in-lifecycle-methods React Docs} */ deprecatedLegacyContext?: any, ) => Component); + /** + * A readonly ref container where {@link current} cannot be mutated. + * + * Created by {@link createRef}, or {@link useRef} when passed `null`. + * + * @template T The type of the ref's value. + * + * @example + * + * ```tsx + * const ref = createRef(); + * + * ref.current = document.createElement('div'); // Error + * ``` + */ interface RefObject { readonly current: T | null; } - // Bivariance hack for consistent unsoundness with RefObject + + /** + * A callback fired whenever the ref's value changes. + * + * @template T The type of the ref's value. + * + * @see {@link https://react.dev/reference/react-dom/components/common#ref-callback React Docs} + * + * @example + * + * ```tsx + *

console.log(node)} /> + * ``` + */ type RefCallback = { bivarianceHack(instance: T | null): void }["bivarianceHack"]; + + /** + * A union type of all possible shapes for React refs. + * + * @see {@link RefCallback} + * @see {@link RefObject} + */ + type Ref = RefCallback | RefObject | null; + /** + * A legacy implementation of refs where you can pass a string to a ref prop. + * + * @see {@link https://react.dev/reference/react/Component#refs React Docs} + * + * @example + * + * ```tsx + *
+ * ``` + */ type LegacyRef = string | Ref; /** * Gets the instance type for a React element. The instance will be different for various component types: @@ -99,6 +209,11 @@ declare namespace React { type ComponentState = any; + /** + * A value which uniquely identifies a node among items in an array. + * + * @see {@link https://react.dev/learn/rendering-lists#keeping-list-items-in-order-with-key React Docs} + */ type Key = string | number | bigint; /** @@ -112,7 +227,7 @@ declare namespace React { /** * Allows getting a ref to the component instance. * Once the component unmounts, React will set `ref.current` to `null` (or call the ref with `null` if you passed a callback ref). - * @see https://react.dev/learn/referencing-values-with-refs#refs-and-the-dom + * @see {@link https://react.dev/learn/referencing-values-with-refs#refs-and-the-dom} */ ref?: Ref | undefined; } @@ -120,7 +235,7 @@ declare namespace React { /** * Allows getting a ref to the component instance. * Once the component unmounts, React will set `ref.current` to `null` (or call the ref with `null` if you passed a callback ref). - * @see https://react.dev/learn/referencing-values-with-refs#refs-and-the-dom + * @see {@link https://react.dev/learn/referencing-values-with-refs#refs-and-the-dom} */ ref?: LegacyRef | undefined; } @@ -371,46 +486,151 @@ declare namespace React { children: (value: T) => ReactNode; } - // TODO: similar to how Fragment is actually a symbol, the values returned from createContext, - // forwardRef and memo are actually objects that are treated specially by the renderer; see: - // https://github.com/facebook/react/blob/v16.6.0/packages/react/src/ReactContext.js#L35-L48 - // https://github.com/facebook/react/blob/v16.6.0/packages/react/src/forwardRef.js#L42-L45 - // https://github.com/facebook/react/blob/v16.6.0/packages/react/src/memo.js#L27-L31 - // However, we have no way of telling the JSX parser that it's a JSX element type or its props other than - // by pretending to be a normal component. - // - // We don't just use ComponentType or FunctionComponent types because you are not supposed to attach statics to this - // object, but rather to the original function. + /** + * An object masquerading as a component. These are created by functions + * like {@link forwardRef}, {@link memo}, and {@link createContext}. + * + * In order to make TypeScript work, we pretend that they are normal + * components. + * + * But they are, in fact, not callable - instead, they are objects which + * are treated specially by the renderer. + */ interface ExoticComponent

{ - /** - * **NOTE**: Exotic components are not callable. - */ (props: P): ReactNode; readonly $$typeof: symbol; } + /** + * An {@link ExoticComponent} with a `displayName` property applied to it. + */ interface NamedExoticComponent

extends ExoticComponent

{ + /** + * Used in debugging messages. You might want to set it + * explicitly if you want to display a different name for + * debugging purposes. + * + * @see {@link https://legacy.reactjs.org/docs/react-component.html#displayname Legacy React Docs} + */ displayName?: string | undefined; } + /** + * An {@link ExoticComponent} with a `propTypes` property applied to it. + */ interface ProviderExoticComponent

extends ExoticComponent

{ propTypes?: WeakValidationMap

| undefined; } + /** + * Used to retrieve the type of a context object from a {@link Context}. + * + * @example + * + * ```tsx + * import { createContext } from 'react'; + * + * const MyContext = createContext({ foo: 'bar' }); + * + * type ContextType = ContextType; + * // ContextType = { foo: string } + * ``` + */ type ContextType> = C extends Context ? T : never; - // NOTE: only the Context object itself can get a displayName - // https://github.com/facebook/react-devtools/blob/e0b854e4c/backend/attachRendererFiber.js#L310-L325 + /** + * Wraps your components to specify the value of this context for all components inside. + * + * @see {@link https://react.dev/reference/react/createContext#provider React Docs} + * + * @example + * + * ```tsx + * import { createContext } from 'react'; + * + * const ThemeContext = createContext('light'); + * + * function App() { + * return ( + * + * + * + * ); + * } + * ``` + */ type Provider = ProviderExoticComponent>; + + /** + * The old way to read context, before {@link useContext} existed. + * + * @see {@link https://react.dev/reference/react/createContext#consumer React Docs} + * + * @example + * + * ```tsx + * import { UserContext } from './user-context'; + * + * function Avatar() { + * return ( + * + * {user => {user.name}} + * + * ); + * } + * ``` + */ type Consumer = ExoticComponent>; + + /** + * Context lets components pass information deep down without explicitly + * passing props. + * + * Created from {@link createContext} + * + * @see {@link https://react.dev/learn/passing-data-deeply-with-context React Docs} + * @see {@link https://react-typescript-cheatsheet.netlify.app/docs/basic/getting-started/context/ React TypeScript Cheatsheet} + * + * @example + * + * ```tsx + * import { createContext } from 'react'; + * + * const ThemeContext = createContext('light'); + * ``` + */ interface Context { Provider: Provider; Consumer: Consumer; + /** + * Used in debugging messages. You might want to set it + * explicitly if you want to display a different name for + * debugging purposes. + * + * @see {@link https://legacy.reactjs.org/docs/react-component.html#displayname Legacy React Docs} + */ displayName?: string | undefined; } + + /** + * Lets you create a {@link Context} that components can provide or read. + * + * @param defaultValue The value you want the context to have when there is no matching + * {@link Provider} in the tree above the component reading the context. This is meant + * as a "last resort" fallback. + * + * @see {@link https://react.dev/reference/react/createContext#reference React Docs} + * @see {@link https://react-typescript-cheatsheet.netlify.app/docs/basic/getting-started/context/ React TypeScript Cheatsheet} + * + * @example + * + * ```tsx + * import { createContext } from 'react'; + * + * const ThemeContext = createContext('light'); + * ``` + */ function createContext( - // If you thought this should be optional, see - // https://github.com/DefinitelyTyped/DefinitelyTyped/pull/24509#issuecomment-382213106 defaultValue: T, ): Context; @@ -427,9 +647,57 @@ declare namespace React { only(children: C): C extends any[] ? never : C; toArray(children: ReactNode | ReactNode[]): Array>; }; + /** + * Lets you group elements without a wrapper node. + * + * @see {@link https://react.dev/reference/react/Fragment React Docs} + * + * @example + * + * ```tsx + * import { Fragment } from 'react'; + * + * + * Hello + * World + * + * ``` + * + * @example + * + * ```tsx + * // Using the <> shorthand syntax: + * + * <> + * Hello + * World + * + * ``` + */ const Fragment: ExoticComponent<{ children?: ReactNode | undefined }>; + + /** + * Lets you find common bugs in your components early during development. + * + * @see {@link https://react.dev/reference/react/StrictMode React Docs} + * + * @example + * + * ```tsx + * import { StrictMode } from 'react'; + * + * + * + * + * ``` + */ const StrictMode: ExoticComponent<{ children?: ReactNode | undefined }>; + /** + * The props accepted by {@link Suspense}. + * + * @see {@link https://react.dev/reference/react/Suspense React Docs} + */ interface SuspenseProps { children?: ReactNode | undefined; @@ -437,27 +705,105 @@ declare namespace React { fallback?: ReactNode; } + /** + * Lets you display a fallback until its children have finished loading. + * + * @see {@link https://react.dev/reference/react/Suspense React Docs} + * + * @example + * + * ```tsx + * import { Suspense } from 'react'; + * + * }> + * + * + * ``` + */ const Suspense: ExoticComponent; const version: string; /** - * {@link https://react.dev/reference/react/Profiler#onrender-callback Profiler API} + * The callback passed to {@link ProfilerProps.onRender}. + * + * @see {@link https://react.dev/reference/react/Profiler#onrender-callback React Docs} */ type ProfilerOnRenderCallback = ( + /** + * The string id prop of the {@link Profiler} tree that has just committed. This lets + * you identify which part of the tree was committed if you are using multiple + * profilers. + * + * @see {@link https://react.dev/reference/react/Profiler#onrender-callback React Docs} + */ id: string, + /** + * This lets you know whether the tree has just been mounted for the first time + * or re-rendered due to a change in props, state, or hooks. + * + * @see {@link https://react.dev/reference/react/Profiler#onrender-callback React Docs} + */ phase: "mount" | "update" | "nested-update", + /** + * The number of milliseconds spent rendering the {@link Profiler} and its descendants + * for the current update. This indicates how well the subtree makes use of + * memoization (e.g. {@link memo} and {@link useMemo}). Ideally this value should decrease + * significantly after the initial mount as many of the descendants will only need to + * re-render if their specific props change. + * + * @see {@link https://react.dev/reference/react/Profiler#onrender-callback React Docs} + */ actualDuration: number, + /** + * The number of milliseconds estimating how much time it would take to re-render the entire + * {@link Profiler} subtree without any optimizations. It is calculated by summing up the most + * recent render durations of each component in the tree. This value estimates a worst-case + * cost of rendering (e.g. the initial mount or a tree with no memoization). Compare + * {@link actualDuration} against it to see if memoization is working. + * + * @see {@link https://react.dev/reference/react/Profiler#onrender-callback React Docs} + */ baseDuration: number, + /** + * A numeric timestamp for when React began rendering the current update. + * + * @see {@link https://react.dev/reference/react/Profiler#onrender-callback React Docs} + */ startTime: number, + /** + * A numeric timestamp for when React committed the current update. This value is shared + * between all profilers in a commit, enabling them to be grouped if desirable. + * + * @see {@link https://react.dev/reference/react/Profiler#onrender-callback React Docs} + */ commitTime: number, interactions: Set, ) => void; + + /** + * The props accepted by {@link Profiler}. + * + * @see {@link https://react.dev/reference/react/Profiler React Docs} + */ interface ProfilerProps { children?: ReactNode | undefined; id: string; onRender: ProfilerOnRenderCallback; } + /** + * Lets you measure rendering performance of a React tree programmatically. + * + * @see {@link https://react.dev/reference/react/Profiler#onrender-callback React Docs} + * + * @example + * + * ```tsx + * + * + * + * ``` + */ const Profiler: ExoticComponent; // @@ -488,7 +834,7 @@ declare namespace React { * } * ``` * - * @see https://react.dev/reference/react/Component#static-contexttype + * @see {@link https://react.dev/reference/react/Component#static-contexttype} */ static contextType?: Context | undefined; @@ -505,14 +851,14 @@ declare namespace React { * declare context: React.ContextType * ``` * - * @see https://react.dev/reference/react/Component#context + * @see {@link https://react.dev/reference/react/Component#context} */ context: unknown; constructor(props: Readonly

| P); /** * @deprecated - * @see https://legacy.reactjs.org/docs/legacy-context.html + * @see {@link https://legacy.reactjs.org/docs/legacy-context.html} */ constructor(props: P, context: any); @@ -542,6 +888,9 @@ declare namespace React { /** * @deprecated Use `ClassicComponent` from `create-react-class` + * + * @see {@link https://legacy.reactjs.org/docs/react-without-es6.html Legacy React Docs} + * @see {@link https://www.npmjs.com/package/create-react-class `create-react-class` on npm} */ interface ClassicComponent

extends Component { replaceState(nextState: S, callback?: () => void): void; @@ -557,23 +906,140 @@ declare namespace React { // Class Interfaces // ---------------------------------------------------------------------- + /** + * Represents the type of a function component. Can optionally + * receive a type argument that represents the props the component + * receives. + * + * @template P The props the component accepts. + * @see {@link https://react-typescript-cheatsheet.netlify.app/docs/basic/getting-started/function_components React TypeScript Cheatsheet} + * @alias for {@link FunctionComponent} + * + * @example + * + * ```tsx + * // With props: + * type Props = { name: string } + * + * const MyComponent: FC = (props) => { + * return

{props.name}
+ * } + * ``` + * + * @example + * + * ```tsx + * // Without props: + * const MyComponentWithoutProps: FC = () => { + * return
MyComponentWithoutProps
+ * } + * ``` + */ type FC

= FunctionComponent

; + /** + * Represents the type of a function component. Can optionally + * receive a type argument that represents the props the component + * accepts. + * + * @template P The props the component accepts. + * @see {@link https://react-typescript-cheatsheet.netlify.app/docs/basic/getting-started/function_components React TypeScript Cheatsheet} + * + * @example + * + * ```tsx + * // With props: + * type Props = { name: string } + * + * const MyComponent: FunctionComponent = (props) => { + * return

{props.name}
+ * } + * ``` + * + * @example + * + * ```tsx + * // Without props: + * const MyComponentWithoutProps: FunctionComponent = () => { + * return
MyComponentWithoutProps
+ * } + * ``` + */ interface FunctionComponent

{ (props: P, context?: any): ReactNode; + /** + * Used to declare the types of the props accepted by the + * component. These types will be checked during rendering + * and in development only. + * + * We recommend using TypeScript instead of checking prop + * types at runtime. + * + * @see {@link https://react.dev/reference/react/Component#static-proptypes React Docs} + */ propTypes?: WeakValidationMap

| undefined; + /** + * @deprecated + * + * Lets you specify which legacy context is consumed by + * this component. + * + * @see {@link https://legacy.reactjs.org/docs/legacy-context.html Legacy React Docs} + */ contextTypes?: ValidationMap | undefined; + /** + * Used to define default values for the props accepted by + * the component. + * + * @see {@link https://react.dev/reference/react/Component#static-defaultprops React Docs} + * + * @example + * + * ```tsx + * type Props = { name?: string } + * + * const MyComponent: FC = (props) => { + * return

{props.name}
+ * } + * + * MyComponent.defaultProps = { + * name: 'John Doe' + * } + * ``` + */ defaultProps?: Partial

| undefined; + /** + * Used in debugging messages. You might want to set it + * explicitly if you want to display a different name for + * debugging purposes. + * + * @see {@link https://legacy.reactjs.org/docs/react-component.html#displayname Legacy React Docs} + * + * @example + * + * ```tsx + * + * const MyComponent: FC = () => { + * return

Hello!
+ * } + * + * MyComponent.displayName = 'MyAwesomeComponent' + * ``` + */ displayName?: string | undefined; } /** - * @deprecated - Equivalent with `React.FC`. + * @deprecated - Equivalent to {@link React.FunctionComponent}. + * + * @see {@link React.FunctionComponent} */ type VFC

= VoidFunctionComponent

; /** - * @deprecated - Equivalent with `React.FunctionComponent`. + * @deprecated - Equivalent to {@link React.FunctionComponent}. + * + * @see {@link React.FunctionComponent} */ interface VoidFunctionComponent

{ (props: P, context?: any): ReactNode; @@ -583,35 +1049,112 @@ declare namespace React { displayName?: string | undefined; } + /** + * The type of the ref received by a {@link ForwardRefRenderFunction}. + * + * @see {@link ForwardRefRenderFunction} + */ type ForwardedRef = ((instance: T | null) => void) | MutableRefObject | null; + /** + * The type of the function passed to {@link forwardRef}. This is considered different + * to a normal {@link FunctionComponent} because it receives an additional argument, + * + * @param props Props passed to the component, if any. + * @param ref A ref forwarded to the component of type {@link ForwardedRef}. + * + * @template T The type of the forwarded ref. + * @template P The type of the props the component accepts. + * + * @see {@link https://react-typescript-cheatsheet.netlify.app/docs/basic/getting-started/forward_and_create_ref/ React TypeScript Cheatsheet} + * @see {@link forwardRef} + */ interface ForwardRefRenderFunction { (props: P, ref: ForwardedRef): ReactNode; + /** + * Used in debugging messages. You might want to set it + * explicitly if you want to display a different name for + * debugging purposes. + * + * Will show `ForwardRef(${Component.displayName || Component.name})` + * in devtools by default, but can be given its own specific name. + * + * @see {@link https://legacy.reactjs.org/docs/react-component.html#displayname Legacy React Docs} + */ displayName?: string | undefined; - // explicit rejected with `never` required due to - // https://github.com/microsoft/TypeScript/issues/36826 /** - * defaultProps are not supported on render functions + * defaultProps are not supported on render functions passed to forwardRef. + * + * @see {@link https://github.com/microsoft/TypeScript/issues/36826 linked GitHub issue} for context + * @see {@link https://react.dev/reference/react/Component#static-defaultprops React Docs} */ defaultProps?: never | undefined; /** - * propTypes are not supported on render functions + * propTypes are not supported on render functions passed to forwardRef. + * + * @see {@link https://github.com/microsoft/TypeScript/issues/36826 linked GitHub issue} for context + * @see {@link https://react.dev/reference/react/Component#static-proptypes React Docs} */ propTypes?: never | undefined; } + /** + * Represents a component class in React. + * + * @template P The props the component accepts. + * @template S The internal state of the component. + */ interface ComponentClass

extends StaticLifecycle { new(props: P, context?: any): Component; + /** + * Used to declare the types of the props accepted by the + * component. These types will be checked during rendering + * and in development only. + * + * We recommend using TypeScript instead of checking prop + * types at runtime. + * + * @see {@link https://react.dev/reference/react/Component#static-proptypes React Docs} + */ propTypes?: WeakValidationMap

| undefined; contextType?: Context | undefined; + /** + * @deprecated use {@link ComponentClass.contextType} instead + * + * Lets you specify which legacy context is consumed by + * this component. + * + * @see {@link https://legacy.reactjs.org/docs/legacy-context.html Legacy React Docs} + */ contextTypes?: ValidationMap | undefined; + /** + * @deprecated + * + * @see {@link https://legacy.reactjs.org/docs/legacy-context.html#how-to-use-context Legacy React Docs} + */ childContextTypes?: ValidationMap | undefined; + /** + * Used to define default values for the props accepted by + * the component. + * + * @see {@link https://react.dev/reference/react/Component#static-defaultprops React Docs} + */ defaultProps?: Partial

| undefined; + /** + * Used in debugging messages. You might want to set it + * explicitly if you want to display a different name for + * debugging purposes. + * + * @see {@link https://legacy.reactjs.org/docs/react-component.html#displayname Legacy React Docs} + */ displayName?: string | undefined; } /** * @deprecated Use `ClassicComponentClass` from `create-react-class` + * + * @see {@link https://legacy.reactjs.org/docs/react-without-es6.html Legacy React Docs} + * @see {@link https://www.npmjs.com/package/create-react-class `create-react-class` on npm} */ interface ClassicComponentClass

extends ComponentClass

{ new(props: P, context?: any): ClassicComponent; @@ -619,9 +1162,13 @@ declare namespace React { } /** - * We use an intersection type to infer multiple type parameters from + * Used in {@link createElement} and {@link createFactory} to represent + * a class. + * + * An intersection type is used to infer multiple type parameters from * a single argument, which is useful for many top-level API defs. - * See https://github.com/Microsoft/TypeScript/issues/7234 for more info. + * See {@link https://github.com/Microsoft/TypeScript/issues/7234 this GitHub issue} + * for more info. */ type ClassType, C extends ComponentClass

> = & C @@ -713,8 +1260,8 @@ declare namespace React { * prevents this from being invoked. * * @deprecated 16.3, use componentDidMount or the constructor instead; will stop working in React 17 - * @see https://legacy.reactjs.org/blog/2018/03/27/update-on-async-rendering.html#initializing-state - * @see https://legacy.reactjs.org/blog/2018/03/27/update-on-async-rendering.html#gradual-migration-path + * @see {@link https://legacy.reactjs.org/blog/2018/03/27/update-on-async-rendering.html#initializing-state} + * @see {@link https://legacy.reactjs.org/blog/2018/03/27/update-on-async-rendering.html#gradual-migration-path} */ componentWillMount?(): void; /** @@ -727,8 +1274,8 @@ declare namespace React { * prevents this from being invoked. * * @deprecated 16.3, use componentDidMount or the constructor instead - * @see https://legacy.reactjs.org/blog/2018/03/27/update-on-async-rendering.html#initializing-state - * @see https://legacy.reactjs.org/blog/2018/03/27/update-on-async-rendering.html#gradual-migration-path + * @see {@link https://legacy.reactjs.org/blog/2018/03/27/update-on-async-rendering.html#initializing-state} + * @see {@link https://legacy.reactjs.org/blog/2018/03/27/update-on-async-rendering.html#gradual-migration-path} */ UNSAFE_componentWillMount?(): void; /** @@ -742,8 +1289,8 @@ declare namespace React { * prevents this from being invoked. * * @deprecated 16.3, use static getDerivedStateFromProps instead; will stop working in React 17 - * @see https://legacy.reactjs.org/blog/2018/03/27/update-on-async-rendering.html#updating-state-based-on-props - * @see https://legacy.reactjs.org/blog/2018/03/27/update-on-async-rendering.html#gradual-migration-path + * @see {@link https://legacy.reactjs.org/blog/2018/03/27/update-on-async-rendering.html#updating-state-based-on-props} + * @see {@link https://legacy.reactjs.org/blog/2018/03/27/update-on-async-rendering.html#gradual-migration-path} */ componentWillReceiveProps?(nextProps: Readonly

, nextContext: any): void; /** @@ -759,8 +1306,8 @@ declare namespace React { * prevents this from being invoked. * * @deprecated 16.3, use static getDerivedStateFromProps instead - * @see https://legacy.reactjs.org/blog/2018/03/27/update-on-async-rendering.html#updating-state-based-on-props - * @see https://legacy.reactjs.org/blog/2018/03/27/update-on-async-rendering.html#gradual-migration-path + * @see {@link https://legacy.reactjs.org/blog/2018/03/27/update-on-async-rendering.html#updating-state-based-on-props} + * @see {@link https://legacy.reactjs.org/blog/2018/03/27/update-on-async-rendering.html#gradual-migration-path} */ UNSAFE_componentWillReceiveProps?(nextProps: Readonly

, nextContext: any): void; /** @@ -772,8 +1319,8 @@ declare namespace React { * prevents this from being invoked. * * @deprecated 16.3, use getSnapshotBeforeUpdate instead; will stop working in React 17 - * @see https://legacy.reactjs.org/blog/2018/03/27/update-on-async-rendering.html#reading-dom-properties-before-an-update - * @see https://legacy.reactjs.org/blog/2018/03/27/update-on-async-rendering.html#gradual-migration-path + * @see {@link https://legacy.reactjs.org/blog/2018/03/27/update-on-async-rendering.html#reading-dom-properties-before-an-update} + * @see {@link https://legacy.reactjs.org/blog/2018/03/27/update-on-async-rendering.html#gradual-migration-path} */ componentWillUpdate?(nextProps: Readonly

, nextState: Readonly, nextContext: any): void; /** @@ -787,8 +1334,8 @@ declare namespace React { * prevents this from being invoked. * * @deprecated 16.3, use getSnapshotBeforeUpdate instead - * @see https://legacy.reactjs.org/blog/2018/03/27/update-on-async-rendering.html#reading-dom-properties-before-an-update - * @see https://legacy.reactjs.org/blog/2018/03/27/update-on-async-rendering.html#gradual-migration-path + * @see {@link https://legacy.reactjs.org/blog/2018/03/27/update-on-async-rendering.html#reading-dom-properties-before-an-update} + * @see {@link https://legacy.reactjs.org/blog/2018/03/27/update-on-async-rendering.html#gradual-migration-path} */ UNSAFE_componentWillUpdate?(nextProps: Readonly

, nextState: Readonly, nextContext: any): void; } @@ -812,7 +1359,9 @@ declare namespace React { } /** - * @deprecated https://legacy.reactjs.org/blog/2016/07/13/mixins-considered-harmful.html + * @deprecated + * + * @see {@link https://legacy.reactjs.org/blog/2016/07/13/mixins-considered-harmful.html Mixins Considered Harmful} */ interface ComponentSpec extends Mixin { render(): ReactNode; @@ -822,13 +1371,45 @@ declare namespace React { function createRef(): RefObject; - // will show `ForwardRef(${Component.displayName || Component.name})` in devtools by default, - // but can be given its own specific name + /** + * The type of the component returned from {@link forwardRef}. + * + * @template P The props the component accepts, if any. + * + * @see {@link ExoticComponent} + */ interface ForwardRefExoticComponent

extends NamedExoticComponent

{ defaultProps?: Partial

| undefined; propTypes?: WeakValidationMap

| undefined; } + /** + * Lets your component expose a DOM node to a parent component + * using a ref. + * + * @see {@link https://react.dev/reference/react/forwardRef React Docs} + * @see {@link https://react-typescript-cheatsheet.netlify.app/docs/basic/getting-started/forward_and_create_ref/ React TypeScript Cheatsheet} + * + * @param render See the {@link ForwardRefRenderFunction}. + * + * @template T The type of the DOM node. + * @template P The props the component accepts, if any. + * + * @example + * + * ```tsx + * interface Props { + * children?: ReactNode; + * type: "submit" | "button"; + * } + * + * export const FancyButton = forwardRef((props, ref) => ( + * + * )); + * ``` + */ function forwardRef( render: ForwardRefRenderFunction, ): ForwardRefExoticComponent & RefAttributes>; @@ -852,28 +1433,107 @@ declare namespace React { type PropsWithChildren

= P & { children?: ReactNode | undefined }; /** - * NOTE: prefer ComponentPropsWithRef, if the ref is forwarded, - * or ComponentPropsWithoutRef when refs are not supported. + * Used to retrieve the props a component accepts. Can either be passed a string, + * indicating a DOM element (e.g. 'div', 'span', etc.) or the type of a React + * component. + * + * It's usually better to use {@link ComponentPropsWithRef} or {@link ComponentPropsWithoutRef} + * instead of this type, as they let you be explicit about whether or not to include + * the `ref` prop. + * + * @see {@link https://react-typescript-cheatsheet.netlify.app/docs/react-types/componentprops/ React TypeScript Cheatsheet} + * + * @example + * + * ```tsx + * // Retrieves the props an 'input' element accepts + * type InputProps = React.ComponentProps<'input'>; + * ``` + * + * @example + * + * ```tsx + * const MyComponent = (props: { foo: number, bar: string }) =>

; + * + * // Retrieves the props 'MyComponent' accepts + * type MyComponentProps = React.ComponentProps; + * ``` */ type ComponentProps> = T extends JSXElementConstructor ? P : T extends keyof JSX.IntrinsicElements ? JSX.IntrinsicElements[T] : {}; + /** - * Get the props of a component that supports the `ref` prop. + * Used to retrieve the props a component accepts with its ref. Can either be + * passed a string, indicating a DOM element (e.g. 'div', 'span', etc.) or the + * type of a React component. + * + * @see {@link https://react-typescript-cheatsheet.netlify.app/docs/react-types/componentprops/ React TypeScript Cheatsheet} * - * WARNING: Use `CustomComponentPropsWithRef` if you know that `T` is not a host component for better type-checking performance. + * @example + * + * ```tsx + * // Retrieves the props an 'input' element accepts + * type InputProps = React.ComponentPropsWithRef<'input'>; + * ``` + * + * @example + * + * ```tsx + * const MyComponent = (props: { foo: number, bar: string }) =>
; + * + * // Retrieves the props 'MyComponent' accepts + * type MyComponentPropsWithRef = React.ComponentPropsWithRef; + * ``` */ type ComponentPropsWithRef = T extends (new(props: infer P) => Component) ? PropsWithoutRef

& RefAttributes> : PropsWithRef>; /** - * Like `ComponentPropsWithRef` but without support for host components (i.e. just "custom components") to improve type-checking performance. + * Used to retrieve the props a custom component accepts with its ref. + * + * Unlike {@link ComponentPropsWithRef}, this only works with custom + * components, i.e. components you define yourself. This is to improve + * type-checking performance. + * + * @example + * + * ```tsx + * const MyComponent = (props: { foo: number, bar: string }) =>

; + * + * // Retrieves the props 'MyComponent' accepts + * type MyComponentPropsWithRef = React.CustomComponentPropsWithRef; + * ``` */ type CustomComponentPropsWithRef = T extends (new(props: infer P) => Component) ? (PropsWithoutRef

& RefAttributes>) : T extends ((props: infer P, legacyContext?: any) => ReactNode) ? PropsWithRef

: never; + + /** + * Used to retrieve the props a component accepts without its ref. Can either be + * passed a string, indicating a DOM element (e.g. 'div', 'span', etc.) or the + * type of a React component. + * + * @see {@link https://react-typescript-cheatsheet.netlify.app/docs/react-types/componentprops/ React TypeScript Cheatsheet} + * + * @example + * + * ```tsx + * // Retrieves the props an 'input' element accepts + * type InputProps = React.ComponentPropsWithoutRef<'input'>; + * ``` + * + * @example + * + * ```tsx + * const MyComponent = (props: { foo: number, bar: string }) =>

; + * + * // Retrieves the props 'MyComponent' accepts + * type MyComponentPropsWithoutRef = React.ComponentPropsWithoutRef; + * ``` + */ type ComponentPropsWithoutRef = PropsWithoutRef>; type ComponentRef = T extends NamedExoticComponent< @@ -944,14 +1604,14 @@ declare namespace React { * context value, as given by the nearest context provider for the given context. * * @version 16.8.0 - * @see https://react.dev/reference/react/useContext + * @see {@link https://react.dev/reference/react/useContext} */ function useContext(context: Context /*, (not public API) observedBits?: number|boolean */): T; /** * Returns a stateful value, and a function to update it. * * @version 16.8.0 - * @see https://react.dev/reference/react/useState + * @see {@link https://react.dev/reference/react/useState} */ function useState(initialState: S | (() => S)): [S, Dispatch>]; // convenience overload when first argument is omitted @@ -959,7 +1619,7 @@ declare namespace React { * Returns a stateful value, and a function to update it. * * @version 16.8.0 - * @see https://react.dev/reference/react/useState + * @see {@link https://react.dev/reference/react/useState} */ function useState(): [S | undefined, Dispatch>]; /** @@ -970,7 +1630,7 @@ declare namespace React { * updates because you can pass `dispatch` down instead of callbacks. * * @version 16.8.0 - * @see https://react.dev/reference/react/useReducer + * @see {@link https://react.dev/reference/react/useReducer} */ // overload where dispatch could accept 0 arguments. function useReducer, I>( @@ -986,7 +1646,7 @@ declare namespace React { * updates because you can pass `dispatch` down instead of callbacks. * * @version 16.8.0 - * @see https://react.dev/reference/react/useReducer + * @see {@link https://react.dev/reference/react/useReducer} */ // overload where dispatch could accept 0 arguments. function useReducer>( @@ -1002,7 +1662,7 @@ declare namespace React { * updates because you can pass `dispatch` down instead of callbacks. * * @version 16.8.0 - * @see https://react.dev/reference/react/useReducer + * @see {@link https://react.dev/reference/react/useReducer} */ // overload where "I" may be a subset of ReducerState; used to provide autocompletion. // If "I" matches ReducerState exactly then the last overload will allow initializer to be omitted. @@ -1020,7 +1680,7 @@ declare namespace React { * updates because you can pass `dispatch` down instead of callbacks. * * @version 16.8.0 - * @see https://react.dev/reference/react/useReducer + * @see {@link https://react.dev/reference/react/useReducer} */ // overload for free "I"; all goes as long as initializer converts it into "ReducerState". function useReducer, I>( @@ -1036,7 +1696,7 @@ declare namespace React { * updates because you can pass `dispatch` down instead of callbacks. * * @version 16.8.0 - * @see https://react.dev/reference/react/useReducer + * @see {@link https://react.dev/reference/react/useReducer} */ // I'm not sure if I keep this 2-ary or if I make it (2,3)-ary; it's currently (2,3)-ary. @@ -1061,7 +1721,7 @@ declare namespace React { * value around similar to how you’d use instance fields in classes. * * @version 16.8.0 - * @see https://react.dev/reference/react/useRef + * @see {@link https://react.dev/reference/react/useRef} */ function useRef(initialValue: T): MutableRefObject; // convenience overload for refs given as a ref prop as they typically start with a null value @@ -1076,7 +1736,7 @@ declare namespace React { * of the generic argument. * * @version 16.8.0 - * @see https://react.dev/reference/react/useRef + * @see {@link https://react.dev/reference/react/useRef} */ function useRef(initialValue: T | null): RefObject; // convenience overload for potentially undefined initialValue / call with 0 arguments @@ -1089,7 +1749,7 @@ declare namespace React { * value around similar to how you’d use instance fields in classes. * * @version 16.8.0 - * @see https://react.dev/reference/react/useRef + * @see {@link https://react.dev/reference/react/useRef} */ function useRef(): MutableRefObject; /** @@ -1103,7 +1763,7 @@ declare namespace React { * `componentDidMount` and `componentDidUpdate`. * * @version 16.8.0 - * @see https://react.dev/reference/react/useLayoutEffect + * @see {@link https://react.dev/reference/react/useLayoutEffect} */ function useLayoutEffect(effect: EffectCallback, deps?: DependencyList): void; /** @@ -1113,7 +1773,7 @@ declare namespace React { * @param deps If present, effect will only activate if the values in the list change. * * @version 16.8.0 - * @see https://react.dev/reference/react/useEffect + * @see {@link https://react.dev/reference/react/useEffect} */ function useEffect(effect: EffectCallback, deps?: DependencyList): void; // NOTE: this does not accept strings, but this will have to be fixed by removing strings from type Ref @@ -1124,7 +1784,7 @@ declare namespace React { * `useImperativeHandle` should be used with `React.forwardRef`. * * @version 16.8.0 - * @see https://react.dev/reference/react/useImperativeHandle + * @see {@link https://react.dev/reference/react/useImperativeHandle} */ function useImperativeHandle(ref: Ref | undefined, init: () => R, deps?: DependencyList): void; // I made 'inputs' required here and in useMemo as there's no point to memoizing without the memoization key @@ -1134,7 +1794,7 @@ declare namespace React { * has changed. * * @version 16.8.0 - * @see https://react.dev/reference/react/useCallback + * @see {@link https://react.dev/reference/react/useCallback} */ // A specific function type would not trigger implicit any. // See https://github.com/DefinitelyTyped/DefinitelyTyped/issues/52873#issuecomment-845806435 for a comparison between `Function` and more specific types. @@ -1144,7 +1804,7 @@ declare namespace React { * `useMemo` will only recompute the memoized value when one of the `deps` has changed. * * @version 16.8.0 - * @see https://react.dev/reference/react/useMemo + * @see {@link https://react.dev/reference/react/useMemo} */ // allow undefined, but don't make it optional as that is very likely a mistake function useMemo(factory: () => T, deps: DependencyList): T; @@ -1155,7 +1815,7 @@ declare namespace React { * It’s most valuable for custom hooks that are part of shared libraries. * * @version 16.8.0 - * @see https://react.dev/reference/react/useDebugValue + * @see {@link https://react.dev/reference/react/useDebugValue} */ // the name of the custom hook is itself derived from the function name at runtime: // it's just the function name without the "use" prefix. @@ -1185,7 +1845,7 @@ declare namespace React { * * @param value The value that is going to be deferred * - * @see https://react.dev/reference/react/useDeferredValue + * @see {@link https://react.dev/reference/react/useDeferredValue} */ export function useDeferredValue(value: T): T; @@ -1202,7 +1862,7 @@ declare namespace React { * * **If some state update causes a component to suspend, that state update should be wrapped in a transition.** * - * @see https://react.dev/reference/react/useTransition + * @see {@link https://react.dev/reference/react/useTransition} */ export function useTransition(): [boolean, TransitionStartFunction]; @@ -1219,7 +1879,7 @@ declare namespace React { * @param effect Imperative function that can return a cleanup function * @param deps If present, effect will only activate if the values in the list change. * - * @see https://github.com/facebook/react/pull/21913 + * @see {@link https://github.com/facebook/react/pull/21913} */ export function useInsertionEffect(effect: EffectCallback, deps?: DependencyList): void; @@ -1227,7 +1887,7 @@ declare namespace React { * @param subscribe * @param getSnapshot * - * @see https://github.com/reactwg/react-18/discussions/86 + * @see {@link https://github.com/reactwg/react-18/discussions/86} */ // keep in sync with `useSyncExternalStore` from `use-sync-external-store` export function useSyncExternalStore( @@ -2020,12 +2680,12 @@ declare namespace React { // Living Standard /** * Hints at the type of data that might be entered by the user while editing the element or its contents - * @see https://html.spec.whatwg.org/multipage/interaction.html#input-modalities:-the-inputmode-attribute + * @see {@link https://html.spec.whatwg.org/multipage/interaction.html#input-modalities:-the-inputmode-attribute} */ inputMode?: "none" | "text" | "tel" | "url" | "email" | "numeric" | "decimal" | "search" | undefined; /** * Specify that a standard HTML element should behave like a defined custom built-in element - * @see https://html.spec.whatwg.org/multipage/custom-elements.html#attr-is + * @see {@link https://html.spec.whatwg.org/multipage/custom-elements.html#attr-is} */ is?: string | undefined; }