Typescript:对象键的并集类型不能用作该对象的键

问题描述 投票:1回答:3

我有一个用一个参数调用的函数。此参数是对象。此函数返回另一个对象,其中一个属性是传递给函数对象的值。当我尝试使用obj [key]表示法检索对象的值时遇到问题。

我确实知道,该密钥必须是正确的类型,才能像这样使用。我不能使用简单的键作为字符串,如果在接口中我没有[key:string]:any。但这不是问题。我的键是作为传递的对象中的键的字符串的并集:'user'| '名称'等。

interface Twitter<T> {
  name: T;
  user: T;
  sex: T extends string ? boolean : string;
}
interface Facebook<T> {
  appName: T;
  whatever: T;
  imjustanexapmle: T;
}

type TwFbObjKeys = keyof Twitter<string> | Facebook<string>

我的功能如下:

public static getArrayOfObjects(
        queryParameters: Twitter<string> | Facebook<string>,
    ) {
        const keys = Object.keys(queryParameters) as TwFbObjKeys[];
        return keys.map((paramId) => ({
            paramId,
            value: queryParameters[paramId],
        }));
    }

我希望使用类型为'name'| “性别” | 'appName'| ...作为具有此键的对象的键,不会引发错误。但不幸的是我有错误:

TS7053:元素隐式地具有“ any”类型,因为类型为“ name”的表达式| “性别” | 'appName'| ...不能用于索引Twitter |脸书类型“ Twitter”上不存在属性“名称”脸书

我正在与它战斗几个小时。知道我该如何解决吗?

typescript
3个回答
1
投票

我认为您的问题来自那条线

type TwFbObjKeys = keyof Twitter<string> | Facebook<string>
// is of type type TwFbObjKeys = "name" | "user" | "sex" | Facebook<string>

您可能想要

type TwFbObjKeys = keyof Twitter<string> | keyof Facebook<string>
// of type type TwFbObjKeys = "name" | "user" | "sex" | "appName" | "whatever" | "imjustanexapmle"

0
投票

(假设您是指type TwFbObjKeys = keyof Twitter<string> | keyof Facebook<string>

Typescript引发错误,因为paramId具有包含两个接口的所有键的类型:

'name'| '用户'| “性别” | 'appName'| “随便什么” | 'imjustanexapmle'

但是queryParameters只能是任一接口的类型:

'name'| '用户'| '性别'

'appName'| “随便什么” | 'imjustanexapmle'

要解决此问题,您应该将两种类型分开:

interface Twitter<T> {
  name: T;
  user: T;
  sex: T extends string ? boolean : string;
}
interface Facebook<T> {
  appName: T;
  whatever: T;
  imjustanexapmle: T;
}

function isTwitter<T>(val: Twitter<T> | Facebook<T>): val is Twitter<T> {
  return (
    typeof (val as Twitter<T>).name !== "undefined" &&
    typeof (val as Twitter<T>).user !== "undefined" &&
    typeof (val as Twitter<T>).sex !== "undefined" 
  )
}

function isFacebook<T>(val: Twitter<T> | Facebook<T>): val is Facebook<T> {
  return (
    typeof (val as Facebook<T>).appName !== "undefined" &&
    typeof (val as Facebook<T>).whatever !== "undefined" &&
    typeof (val as Facebook<T>).imjustanexapmle !== "undefined" 
  )
}

function getValue<T, K extends keyof T>(obj: T, key: K) {
  return obj[key];
}

function getArrayOfObjects(
    queryParameters: Twitter<string> | Facebook<string>,
) {
  if (isTwitter(queryParameters)) { // if queryParameters is of type Twitter<string>
    const keys = Object.keys(queryParameters);
    return keys.map((paramId) => ({
      paramId,
      value: getValue(queryParameters, paramId as keyof Twitter<string>)
    }));
  } else if (isFacebook(queryParameters)) { // if queryParameters is of type Facebook<string>
    const keys = Object.keys(queryParameters);
    return keys.map((paramId) => ({ 
      paramId,
      value: getValue(queryParameters, paramId as keyof Facebook<string>)
    }));
  } else {  // if queryParameters is invalid
    throw new Error("invalid queryParameters!")
  }
}

0
投票

最好将函数参数queryParameters定义为具有约束T而不是固定并集<T extends Twitter<string> | Facebook<string>>的泛型queryParameters: Twitter<string> | Facebook<string>

function getArrayOfObjects<T extends Twitter<string> | Facebook<string>>(
    queryParameters: T
) {
    const keys = Object.keys(queryParameters) as (keyof T)[];
    return keys.map((paramId) => ({
        paramId,
        value: queryParameters[paramId],
    }));
}

Playground

说明

通过使用泛型,您可以为queryParameters保留传入参数的类型,而只是确保它是Twitter<string> | Facebook<string>的子类型。对于联合类型Twitter<string> | Facebook<string>queryParameters仍将不确定,并且在函数体内仍可能是Twitter<string>Facebook<string>,这会导致keyof运算符出现问题。

您可能打算将TwFbObjKeys定义为

// "name" | "user" | "sex" | "appName" | "whatever" | "imjustanexapmle"
type TwFbObjKeys = keyof Twitter<string> | keyof Facebook<string>

// not this: keyof only applies to Twitter<string> here: "name" | "user" | "sex" | Facebook<string>
type TwFbObjKeys_not = keyof Twitter<string> | Facebook<string>

但是那不能单独解决您的问题,因为给定联合类型,您只能访问所有组成部分的common属性键。示例:

declare const queryParameters: Twitter<string> | Facebook<string>

// type keysOfQueryParameters = never
type keysOfQueryParameters = keyof typeof queryParameters

// ok, let's try to define all union keys manually
type TwFbObjKeys = "name" | "user" | "sex" | "appName" | "whatever" | "imjustanexapmle"
declare const unionKeys: TwFbObjKeys

// error: doesn't work either, because Twitter and Facebook don't have common props
queryParameters[unionKeys]

// this would work, if both Twitter and Facebook have "common" prop
queryParameters["common"]

Playground

© www.soinside.com 2019 - 2024. All rights reserved.