【问题标题】:TypeScript function that takes an object without certain keysTypeScript 函数接受一个没有特定键的对象
【发布时间】:2018-10-03 09:34:42
【问题描述】:

尝试创建一个函数,该函数接受一个不能有某些键集的单个参数对象,而不需要人们手动指定类型。我试图以某种方式利用never,但我正忙于理解推理如何适用于接受具有泛型参数的类型的函数参数。

interface ReservedAttributes {
    someKey: string
}

// evaluates to never if the type has a key that intersects 
// with the keys in ReservedAttributes
type ValidAttributes<T> = keyof T extends Exclude<keyof T, keyof ReservedAttributes> ? T : never

// This is correctly a never, but it doesn't address this problem
type Test = ValidAttributes<{someKey : string}>


// This doesn't work because the function argument ends 
// up being inferred as { [name: string] : string}
function foo1<K extends { [name: string] : string}>(attributes: ValidAttributes<K>)  {
    // ...
}
foo1({a: 'hi', someKey: ''})

// Roughly equivalent to the above
function foo2< K extends { [name: string] : string}, V extends ValidAttributes<K> >(attributes: V)  {
    // ...
}

// This one allows V to correctly evaluate to never, 
// but I'm not sure how to leverage that
function foo3< K extends { [name: string] : string}, V extends ValidAttributes<K> >(attributes: K)  {
    // ...
}
foo3({a: 'hi', someKey: ''})

你会如何解决这个问题?

【问题讨论】:

    标签: typescript


    【解决方案1】:

    我想我会先尝试远离泛型和条件类型,然后做这样的事情:

    type ValidAttributes = Record<string, string> &
      Partial<Record<keyof ReservedAttributes, never>>;
    declare function foo(attributes: ValidAttributes): void;
    
    foo({ a: 'hi' }); // okay
    foo({ a: 'hi', someKey: '' }); // error, someKey
    

    在这种情况下,ValidAttributes 是一个通用字符串值字典,但来自ReservedAttributes 的键被列为never 类型的可选属性(允许缺少可选属性,never 类型的属性确实不允许存在,因此never 类型的可选属性或多或少是必须缺失的。)这对您有用吗?

    如果您需要在泛型约束中使用条件类型,可以这样做:

    type Attributes<K extends keyof any> = {
      [P in K]: P extends keyof ReservedAttributes ? never: string
    };
    declare function foo<T>(attributes: T & Attributes<keyof T>): void;
    foo({ a: 'hi' }); // okay
    foo({ a: 'hi', someKey: '' }) // error
    

    但它更复杂,并且达到了类似的结果。希望有帮助;祝你好运!

    【讨论】:

    • 这很有创意!我可能过度应用了最近才了解它们的条件类型。谢谢你。您是否将keyof any 用作string | number | symbol 的简写?
    • 在这种情况下为什么要避免使用条件类型?只是因为你认为这更难推理?
    • 是的,keyof any 更容易编写,并且向后兼容仅将字符串视为键的 typescript 版本。
    • 对于开发人员和编译器来说,条件类型和泛型更难推理。这意味着您可能会发现自己不得不费尽心思让编译器相信表达式可以分配给这种类型。无论哪种方式,这都不是什么大不了的事,真的。
    【解决方案2】:

    一种可能的方法是使用void 类型声明具有可选属性的类型。没有类型可以分配给void,因此任何具有这些属性的对象都不会与参数类型兼容:

    interface ReservedAttributes {
        someKey: string;
    }
    
    type DisallowedAttributes<T> = { [k in keyof T]?: void };
    
    
    type AllowedKeys = string; // could be restricted if necessary 
    
    function fn<K extends AllowedKeys>(t: { [k in K]: {}} & DisallowedAttributes<ReservedAttributes>) {
    
    }
    
    fn({ a: 'hi' }); // ok
    
    fn({ b: 4, someKey: 'd' }); // Error: Type 'string' is not assignable to type 'void & {}'.
                                // Type 'string' is not assignable to type 'void'.
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-10-13
      • 1970-01-01
      • 2023-01-12
      • 2021-03-17
      • 1970-01-01
      • 2016-07-16
      • 2020-03-31
      • 1970-01-01
      相关资源
      最近更新 更多