enhance(backend): enhance SchemaType handling of anyOf (#9762)
* enhance(backend): enhance anyOf handling * clean up
This commit is contained in:
parent
387fcd5c5d
commit
7c3143b8e5
|
@ -132,11 +132,27 @@ type NullOrUndefined<p extends Schema, T> =
|
||||||
// https://stackoverflow.com/questions/54938141/typescript-convert-union-to-intersection
|
// https://stackoverflow.com/questions/54938141/typescript-convert-union-to-intersection
|
||||||
// Get intersection from union
|
// Get intersection from union
|
||||||
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never;
|
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never;
|
||||||
|
type PartialIntersection<T> = Partial<UnionToIntersection<T>>;
|
||||||
|
|
||||||
// https://github.com/misskey-dev/misskey/pull/8144#discussion_r785287552
|
// https://github.com/misskey-dev/misskey/pull/8144#discussion_r785287552
|
||||||
// To get union, we use `Foo extends any ? Hoge<Foo> : never`
|
// To get union, we use `Foo extends any ? Hoge<Foo> : never`
|
||||||
type UnionSchemaType<a extends readonly any[], X extends Schema = a[number]> = X extends any ? SchemaType<X> : never;
|
type UnionSchemaType<a extends readonly any[], X extends Schema = a[number]> = X extends any ? SchemaType<X> : never;
|
||||||
type ArrayUnion<T> = T extends any ? Array<T> : never;
|
type UnionObjectSchemaType<a extends readonly any[], X extends Schema = a[number]> = X extends any ? ObjectSchemaType<X> : never;
|
||||||
|
type ArrayUnion<T> = T extends any ? Array<T> : never;
|
||||||
|
|
||||||
|
type ObjectSchemaTypeDef<p extends Schema> =
|
||||||
|
p['ref'] extends keyof typeof refs ? Packed<p['ref']> :
|
||||||
|
p['properties'] extends NonNullable<Obj> ?
|
||||||
|
p['anyOf'] extends ReadonlyArray<Schema> ?
|
||||||
|
ObjType<p['properties'], NonNullable<p['required']>[number]> & UnionObjectSchemaType<p['anyOf']> & PartialIntersection<UnionObjectSchemaType<p['anyOf']>>
|
||||||
|
:
|
||||||
|
ObjType<p['properties'], NonNullable<p['required']>[number]>
|
||||||
|
:
|
||||||
|
p['anyOf'] extends ReadonlyArray<Schema> ? UnionObjectSchemaType<p['anyOf']> & PartialIntersection<UnionObjectSchemaType<p['anyOf']>> :
|
||||||
|
p['allOf'] extends ReadonlyArray<Schema> ? UnionToIntersection<UnionSchemaType<p['allOf']>> :
|
||||||
|
any
|
||||||
|
|
||||||
|
type ObjectSchemaType<p extends Schema> = NullOrUndefined<p, ObjectSchemaTypeDef<p>>;
|
||||||
|
|
||||||
export type SchemaTypeDef<p extends Schema> =
|
export type SchemaTypeDef<p extends Schema> =
|
||||||
p['type'] extends 'null' ? null :
|
p['type'] extends 'null' ? null :
|
||||||
|
@ -149,13 +165,7 @@ export type SchemaTypeDef<p extends Schema> =
|
||||||
string
|
string
|
||||||
) :
|
) :
|
||||||
p['type'] extends 'boolean' ? boolean :
|
p['type'] extends 'boolean' ? boolean :
|
||||||
p['type'] extends 'object' ? (
|
p['type'] extends 'object' ? ObjectSchemaTypeDef<p> :
|
||||||
p['ref'] extends keyof typeof refs ? Packed<p['ref']> :
|
|
||||||
p['properties'] extends NonNullable<Obj> ? ObjType<p['properties'], NonNullable<p['required']>[number]> :
|
|
||||||
p['anyOf'] extends ReadonlyArray<Schema> ? UnionSchemaType<p['anyOf']> & Partial<UnionToIntersection<UnionSchemaType<p['anyOf']>>> :
|
|
||||||
p['allOf'] extends ReadonlyArray<Schema> ? UnionToIntersection<UnionSchemaType<p['allOf']>> :
|
|
||||||
any
|
|
||||||
) :
|
|
||||||
p['type'] extends 'array' ? (
|
p['type'] extends 'array' ? (
|
||||||
p['items'] extends OfSchema ? (
|
p['items'] extends OfSchema ? (
|
||||||
p['items']['anyOf'] extends ReadonlyArray<Schema> ? UnionSchemaType<NonNullable<p['items']['anyOf']>>[] :
|
p['items']['anyOf'] extends ReadonlyArray<Schema> ? UnionSchemaType<NonNullable<p['items']['anyOf']>>[] :
|
||||||
|
@ -166,6 +176,7 @@ export type SchemaTypeDef<p extends Schema> =
|
||||||
p['items'] extends NonNullable<Schema> ? SchemaTypeDef<p['items']>[] :
|
p['items'] extends NonNullable<Schema> ? SchemaTypeDef<p['items']>[] :
|
||||||
any[]
|
any[]
|
||||||
) :
|
) :
|
||||||
|
p['anyOf'] extends ReadonlyArray<Schema> ? UnionSchemaType<p['anyOf']> & PartialIntersection<UnionSchemaType<p['anyOf']>> :
|
||||||
p['oneOf'] extends ReadonlyArray<Schema> ? UnionSchemaType<p['oneOf']> :
|
p['oneOf'] extends ReadonlyArray<Schema> ? UnionSchemaType<p['oneOf']> :
|
||||||
any;
|
any;
|
||||||
|
|
||||||
|
|
|
@ -90,48 +90,13 @@ export const paramDef = {
|
||||||
visibleUserIds: { type: 'array', uniqueItems: true, items: {
|
visibleUserIds: { type: 'array', uniqueItems: true, items: {
|
||||||
type: 'string', format: 'misskey:id',
|
type: 'string', format: 'misskey:id',
|
||||||
} },
|
} },
|
||||||
text: { type: 'string', maxLength: MAX_NOTE_TEXT_LENGTH, nullable: true },
|
|
||||||
cw: { type: 'string', nullable: true, maxLength: 100 },
|
cw: { type: 'string', nullable: true, maxLength: 100 },
|
||||||
localOnly: { type: 'boolean', default: false },
|
localOnly: { type: 'boolean', default: false },
|
||||||
noExtractMentions: { type: 'boolean', default: false },
|
noExtractMentions: { type: 'boolean', default: false },
|
||||||
noExtractHashtags: { type: 'boolean', default: false },
|
noExtractHashtags: { type: 'boolean', default: false },
|
||||||
noExtractEmojis: { type: 'boolean', default: false },
|
noExtractEmojis: { type: 'boolean', default: false },
|
||||||
fileIds: {
|
|
||||||
type: 'array',
|
|
||||||
uniqueItems: true,
|
|
||||||
minItems: 1,
|
|
||||||
maxItems: 16,
|
|
||||||
items: { type: 'string', format: 'misskey:id' },
|
|
||||||
},
|
|
||||||
mediaIds: {
|
|
||||||
deprecated: true,
|
|
||||||
description: 'Use `fileIds` instead. If both are specified, this property is discarded.',
|
|
||||||
type: 'array',
|
|
||||||
uniqueItems: true,
|
|
||||||
minItems: 1,
|
|
||||||
maxItems: 16,
|
|
||||||
items: { type: 'string', format: 'misskey:id' },
|
|
||||||
},
|
|
||||||
replyId: { type: 'string', format: 'misskey:id', nullable: true },
|
replyId: { type: 'string', format: 'misskey:id', nullable: true },
|
||||||
renoteId: { type: 'string', format: 'misskey:id', nullable: true },
|
|
||||||
channelId: { type: 'string', format: 'misskey:id', nullable: true },
|
channelId: { type: 'string', format: 'misskey:id', nullable: true },
|
||||||
poll: {
|
|
||||||
type: 'object',
|
|
||||||
nullable: true,
|
|
||||||
properties: {
|
|
||||||
choices: {
|
|
||||||
type: 'array',
|
|
||||||
uniqueItems: true,
|
|
||||||
minItems: 2,
|
|
||||||
maxItems: 10,
|
|
||||||
items: { type: 'string', minLength: 1, maxLength: 50 },
|
|
||||||
},
|
|
||||||
multiple: { type: 'boolean', default: false },
|
|
||||||
expiresAt: { type: 'integer', nullable: true },
|
|
||||||
expiredAfter: { type: 'integer', nullable: true, minimum: 1 },
|
|
||||||
},
|
|
||||||
required: ['choices'],
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
anyOf: [
|
anyOf: [
|
||||||
{
|
{
|
||||||
|
@ -143,21 +108,60 @@ export const paramDef = {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// (re)note with files, text and poll are optional
|
// (re)note with files, text and poll are optional
|
||||||
|
properties: {
|
||||||
|
fileIds: {
|
||||||
|
type: 'array',
|
||||||
|
uniqueItems: true,
|
||||||
|
minItems: 1,
|
||||||
|
maxItems: 16,
|
||||||
|
items: { type: 'string', format: 'misskey:id' },
|
||||||
|
},
|
||||||
|
},
|
||||||
required: ['fileIds'],
|
required: ['fileIds'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// (re)note with files, text and poll are optional
|
// (re)note with files, text and poll are optional
|
||||||
|
properties: {
|
||||||
|
mediaIds: {
|
||||||
|
deprecated: true,
|
||||||
|
description: 'Use `fileIds` instead. If both are specified, this property is discarded.',
|
||||||
|
type: 'array',
|
||||||
|
uniqueItems: true,
|
||||||
|
minItems: 1,
|
||||||
|
maxItems: 16,
|
||||||
|
items: { type: 'string', format: 'misskey:id' },
|
||||||
|
},
|
||||||
|
},
|
||||||
required: ['mediaIds'],
|
required: ['mediaIds'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// (re)note with poll, text and files are optional
|
// (re)note with poll, text and files are optional
|
||||||
properties: {
|
properties: {
|
||||||
poll: { type: 'object', nullable: false },
|
poll: {
|
||||||
|
type: 'object',
|
||||||
|
nullable: true,
|
||||||
|
properties: {
|
||||||
|
choices: {
|
||||||
|
type: 'array',
|
||||||
|
uniqueItems: true,
|
||||||
|
minItems: 2,
|
||||||
|
maxItems: 10,
|
||||||
|
items: { type: 'string', minLength: 1, maxLength: 50 },
|
||||||
|
},
|
||||||
|
multiple: { type: 'boolean' },
|
||||||
|
expiresAt: { type: 'integer', nullable: true },
|
||||||
|
expiredAfter: { type: 'integer', nullable: true, minimum: 1 },
|
||||||
|
},
|
||||||
|
required: ['choices'],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
required: ['poll'],
|
required: ['poll'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// pure renote
|
// pure renote
|
||||||
|
properties: {
|
||||||
|
renoteId: { type: 'string', format: 'misskey:id', nullable: true },
|
||||||
|
},
|
||||||
required: ['renoteId'],
|
required: ['renoteId'],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
|
@ -29,14 +29,22 @@ export const meta = {
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
username: { type: 'string', nullable: true },
|
|
||||||
host: { type: 'string', nullable: true },
|
|
||||||
limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
|
limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
|
||||||
detail: { type: 'boolean', default: true },
|
detail: { type: 'boolean', default: true },
|
||||||
},
|
},
|
||||||
anyOf: [
|
anyOf: [
|
||||||
{ required: ['username'] },
|
{
|
||||||
{ required: ['host'] },
|
properties: {
|
||||||
|
username: { type: 'string', nullable: true },
|
||||||
|
},
|
||||||
|
required: ['username']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
properties: {
|
||||||
|
host: { type: 'string', nullable: true },
|
||||||
|
},
|
||||||
|
required: ['host']
|
||||||
|
},
|
||||||
],
|
],
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue