-
Notifications
You must be signed in to change notification settings - Fork 12.9k
Closed
Labels
Needs More InfoThe issue still hasn't been fully clarifiedThe issue still hasn't been fully clarified
Description
TypeScript Version: 3.5.3
Search Terms: inference nested member
Code
The Playground link reproduces this issue, but here is the whole code.
// Issue: the generics are not flowing all the way through.
// This works fine
const handler1 = CommandHandler.create<RespondToQuestion>(
requireThat(
isCollaboratorOnRequest()
),
requireThat(
subjectIs(allowedTo.fooTheBar, BasedOnClaims.fromMessageBody(b => b))
),
async ({ body }) => {
console.log(body.subject_id)
return { version: 1 }
}
)
// But when I start chaining the specifications, the type that has been specified to be `RespondToQuestion` is
// now `unknown`.
const handler2 = CommandHandler.create<RespondToQuestion>(
requireThat(
isCollaboratorOnRequest().or(
subjectIs(allowedTo.fooTheBar, BasedOnClaims.fromMessageBody(b => b))
)
),
async ({ body }) => {
console.log(body.subject_id)
return { version: 1 }
}
)
// Specifying the generic seems to do the trick though, but why do I have to? Shouldn't it flow through?
const handler3 = CommandHandler.create<RespondToQuestion>(
requireThat(
isCollaboratorOnRequest<RespondToQuestion>().or(
subjectIs(allowedTo.fooTheBar, BasedOnClaims.fromMessageBody(b => b))
)
),
async ({ body }) => {
console.log(body.subject_id)
return { version: 1 }
}
)
const msg: Message<RespondToQuestion> = { body: { type: 'RespondToQuestion', subject_id: '123' }, meta: null as any }
handler1(msg)
handler2(msg)
handler3(msg)
// Sorry for all the types 😅
export interface AsyncSpecification<T> {
and(other: AsyncSpecification<T>): AsyncSpecification<T>;
or(other: AsyncSpecification<T>): AsyncSpecification<T>;
execute(value: T): Promise<T>;
}
export declare function isCollaboratorOnRequest<T>(): AsyncSpecification<Message<T>>;
export interface Message<T, M = unknown> {
body: T;
meta: M;
}
export interface Claims {
subject_id: string;
}
export type ClaimsLookupStrategy<T> = (message: Message<T>) => Promise<Claims>;
export type AllowedToBasedOnClaims = (claims: Claims) => Promise<Claims>;
export declare function subjectIs<T>(
checkAllowedToBasedOnClaims: AllowedToBasedOnClaims,
claimsLookupStrategy: ClaimsLookupStrategy<T>
): AsyncSpecification<Message<T>>;
export interface AllowedTo {
fooTheBar: AllowedToBasedOnClaims;
}
export declare type Handler<I, O = unknown> = (input: Message<I>) => Promise<O>;
export declare type Pipe<I, O = unknown> = (input: Message<I>, next: Handler<I, O>) => Promise<O>;
export interface CommandResult {
version: number | null;
}
export interface Command { type: string }
export declare const CommandHandler: {
create: <C extends Command>(...pipes: Pipe<C, CommandResult>[]) => Handler<C, CommandResult>;
};
export declare function requireThat<I, O>(specification: AsyncSpecification<Message<I>>): Pipe<I, O>;
export interface RespondToQuestion extends Command {
type: 'RespondToQuestion'
subject_id: string
}
export declare const allowedTo: AllowedTo
export declare type ClaimsWithOptionalSubjectId = Omit<Claims, 'subject_id'> & {
subject_id?: string;
};
export declare const BasedOnClaims: {
fromMessage<T>(messageBodyToClaims: (message: Message<T, unknown>) => ClaimsWithOptionalSubjectId): ClaimsLookupStrategy<T>;
fromMessageBody<T>(messageBodyToClaims: (body: T) => ClaimsWithOptionalSubjectId): ClaimsLookupStrategy<T>;
};
Expected behavior:
The type argument RespondToQuestion
passed to CommandHandler.create<RespondToQuestion>()
should flow all the way through consistently.
Actual behavior:
When using certain patterns, TS will change the type to unknown
.
Additionally, it seems it can't make up its' mind. 😅 The error says body
can't be inferred, and yet the tip gets it right.
Playground Link: Playground
Metadata
Metadata
Assignees
Labels
Needs More InfoThe issue still hasn't been fully clarifiedThe issue still hasn't been fully clarified