打字稿中的泛型,带有用于类型保护的 typeof

generics in typescript with typeof for type guard

你好,我正在学习打字稿,我想创建一个函数来合并对象或字符串,我使用泛型向函数添加额外的信息,以便理解 mergedObj.name 合并两个对象后,如果我删除了字符串的条件,则工作正常,但如果我尝试处理这两种情况,名称显示错误

Property 'name' does not exist on type 'string | ({ name: string; } & { age: number; })'. Property 'name' does not exist on type 'string'

我理解这里的 mergedObj 类型是

string | ({ name: string; } & { age: number; })

但是我如何使用泛型处理这两种情况或多种类型保护?

type ObjectOrString = object | string;

function merge<T extends ObjectOrString , U extends ObjectOrString>(obj1: T, obj2: U) {
    if(typeof obj1 === 'string' || typeof obj2 === 'string'){
        return obj1.toString() + obj2.toString();
    }
    return Object.assign(obj1, obj2);
}


let mergedObj = merge({ name: "zeyad" }, { age: 22 }) ;

console.log(mergedObj.name); // Error Here 
/*  Property 'name' does not exist on type 'string | ({ name: string; } 
    & { age: number; })'.
    Property 'name' does not exist on type 'string'.ts(2339)
*/

let mergedStr = merge('zeyad', ' moamen');
console.log(mergedStr) // zeyad moamen

您必须在某处消除类型歧义,例如:

if (typeof mergedObj !== 'string')
    console.log(mergedObj.name); 

删除错误

使用 object 类型来表示 hashmap 数据结构不是最佳选择。最好使用 Record<string, unknown> 代替。由于JS中的每一个值都是一个对象,类型object几乎就是any类型。

您的函数有 4 个允许的状态:

type ObjectString = Record<string, unknown> | string


function merge<
  Obj1 extends string,
  Obj2 extends Record<string, unknown>
>(obj1: Obj1, obj2: Obj2): string
function merge<
  Obj1 extends Record<string, unknown>,
  Obj2 extends string
>(obj1: Obj1, obj2: Obj2): string
function merge<
  Obj1 extends string,
  Obj2 extends string
>(obj1: Obj1, obj2: Obj2): `${Obj1}${Obj2}`
function merge<
  Obj1 extends Record<string, unknown>,
  Obj2 extends Record<string, unknown>
>(obj1: Obj1, obj2: Obj2): Obj1 & Obj2
function merge(
  obj1: ObjectString,
  obj2: ObjectString,
): any {
  if (typeof obj1 === 'string' || typeof obj2 === 'string') {
    return obj1.toString + obj2.toString()
  }
  return Object.assign(obj1, obj2)
}

const a = merge('a', 'b') // 'ab'
const b = merge({ a: 1 }, { b: 2 }) // {a:number, b: number}
const c = merge({ a: 1 }, 'c') // string

Playground

对于每个状态,我都编写了重载以确保 return 类型被正确推断。

几种类型不使用通用函数就更好了。因为它增加了额外的不必要的类型检查和逻辑。取而代之的是对不同的类型使用不同的函数。更简单、清晰、安全

type Dict<T> = {
  [key: string]: T
}

function mergeObjects(obj1: Dict<string | number>, obj2: Dict<string | number>): Dict<string | number> {
    return Object.assign(obj1, obj2);
}

function mergeStrings(str1: string, str2: string): string {
    return str1 + str2;
}

const mergedObj = mergeObjects({ name: "zeyad" }, { age: 22 }) ;
console.log(mergedObj.name);

const mergedStr = mergeStrings('zeyad', ' moamen');
console.log(mergedStr)

例如,在您的情况下,我可以将 object 作为第一个参数传递给 merge 函数,将 string 作为第二个参数传递给函数。你的函数将 运行 return Object.assign(obj1, obj2); 并且它会给你意想不到的结果:

const obj1 = {a: 'first'};
const obj2 = 'second';
const merge = Object.assign(obj1, obj2);

console.log(merge);
console.log(merge.constructor.name);

反之亦然:

const obj1 = {a: 'first'};
const obj2 = 'second';
const merge = Object.assign(obj2, obj1);

console.log(merge);
console.log(merge.constructor.name);

答案看起来一样,但它们有不同 instance,这可能是您的应用程序出现意外错误的原因