在柯里函数中给定目标时如何实现结构类型安全



我试图用一个curry函数实现结构类型安全,其中第一个参数是对象的键,返回curry函数,它的参数接受目标(对象)。

我已经准备了一个完全工作的操场,因为在那里更容易解释:打印稿操场

// I have a map of Resources where the keys are equal to the resource it's type
type Resources = {
user: {
id: string
type: 'user'
attributes: {
name: string
age: number
last_name: string
}
},
// uncomment this section to break function underneath
//  usergroup: {
//   id: string
//   type: 'usergroup'
//   attributes: {
//     mosterd: string
//     group: string
//   }
// }
}
type ResourceTypes = keyof Resources
// user|usergroup
type UserAttributes = keyof Resources['user']['attributes']
// name|age|last_name
function constrain<
T extends { type: ResourceTypes },
A extends keyof Resources[T['type']]['attributes']
> (attribute: A) {
return (resource: T) => {
}
}
// Is it possible to get typesafety for the attribute argument 
// when resource argument has been given, like following example?
const result = constrain('name')({ type: 'user' })
type AllAttributes = keyof Resources[ResourceTypes]['attributes']
// becomes never, I assume this is what is happening in the constrain function but why cant it infer the ResourceType?

由于TypeScript的一些神秘行为,你必须构造一个类型来获取联合的所有键,使用相关问题的答案。

编辑:我已经更新了函数的通用约束,以便内部键(AllAttributes类型)与资源类型匹配。
type KeysOfUnion<T> = T extends T ? keyof T : never;
type NoKeys = keyof ({ a: 1 } | { b: 2 }); // never
type AllKeys = KeysOfUnion<{ a: 1 } | { b: 2 }>; // 'a' | 'b'
type AllAttributes = KeysOfUnion<Resources[ResourceTypes]['attributes']>;
// will now be 'name' | 'age' | 'last_name' | 'mosterd' | 'group'
function constrain<
A extends KeysOfUnion<Resources[keyof Resources]['attributes']>,
T extends {
[x in keyof Resources]: Resources[x]['attributes'] extends {
[y in A]: unknown;
}
? x
: never;
}[keyof Resources]
>(attribute: A) {
return (resource: { type: T }) => {};
}
constrain('name')({ type: 'user' }); // OK
constrain('name')({ type: 'usergroup' }); // error
constrain('group')({ type: 'usergroup' }); // OK
constrain('group')({ type: 'user' }); // error

您还可以使用UnionToIntersection<U>(归功于jcalz)并将函数定义为

function constrain<
A extends keyof UnionToIntersection<Resources[keyof Resources]['attributes']>,
T extends Extract<Resources[keyof Resources], { attributes: Record<A, any> }>,
>(attribute: A) {
return (resource: { type: T['type'] }) => {
}
}

游乐场

最新更新