我正在为我的React + Apollo项目使用TypeScript,并且正在使用graphql-code-generator生成以下类型:
type Maybe<T> = T | null;
type Scalars = {
ID: string,
String: string,
Boolean: boolean,
Int: number,
Float: number,
DateTime: any,
ISO8601DateTime: any,
Date: any,
Json: any,
};
type RelayNode = {
id: Scalars['ID'],
};
type Project = RelayNode & {
__typename?: 'Project',
number: Scalars['Int'],
title: Scalars['String']
projectManagers?: Maybe<Array<{ id: Scalars['String'], name: Scalars['String'] }>>,
};
type GetProjectQuery = (
{ __typename?: 'Query' }
& { project: Maybe<(
{ __typename?: 'Project' }
& Pick<Project, 'number' | 'title' | 'projectManagers'>
)> }
);
type ProjectInput = {
title?: Maybe<Scalars['String']>,
number?: Maybe<Scalars['Int']>,
projectManagerIds?: Maybe<Array<Scalars['String']>>,
};
现在,我想构建一个以project
作为初始值的表单。一个示例项目可能看起来像...
const project: GetProjectQuery['project'] = {
__typename: 'Project',
number: 123,
title: 'My awesome project',
projectManagers: [{ id: '123', name: 'Me & myself'}]
}
...但是我的表单仅允许ProjectInput
而不允许Project
(因为输入变量可能与输出变量不同),所以我正在这样做...
const input: ProjectInput = project
...但是这似乎是有效的– Typescript不会引发任何错误。但是我要强制执行警告,该对象不应定义projectManagers
。目标是强制未定义projectManagers
但定义projectManagerIds
的输入对象。
我创建了一个最小的测试用例here,其中“拒绝”有效:
type Project = {
title?: string
number?: number
};
const project: Project = {
title: 'foo',
number: 123,
foo: 'bar' // this key isn't allowed
}
但是我不明白为什么上面的生成类型无法使用这种方法。
答案 0 :(得分:1)
您不能在变量上强制执行此操作。 OOP的基本原理是可以将子类型分配给基本类型引用。由于打字稿使用结构化打字,因此基本类型/子类型的关系不是显式的,但是鉴于GetProjectQuery['project']
和ProjectInput
的结构,GetProjectQuery['project']
是ProjectInput
的子类型
现在,对于非常特定的情况,打字稿有时会故意违反该子类型,而该子类型可分配给基本类型规则:
您的情况都不是这样,所以您不会出错。
如果要在调用函数时执行此验证,那么我们可以使用一些通用类型参数魔术来捕获参数的实际类型并在任何其他属性上强制执行错误:
function withProjectInput<T extends ProjectInput>(p: T & Record<Exclude<keyof T, keyof ProjectInput>, ["No excess properties allowed here"]>) {
}
withProjectInput(project); // error here