通过泛型缩小TypeScript索引类型

时间:2020-06-08 03:16:11

标签: typescript

请考虑以下类型:

type RequestSpecification = {
  type: "get" | "post";
  parameters: { [key: string]: ParameterSpecification }
};

type ParameterSpecification = {
    required: boolean;
    // ...
}

现在,它可以像这样创建变量:

const requestSpecification: RequestSpecification = {
    type: "get",
    parameters: {
        id: {
            required: true
        },
        nickname: {
            required: false
        }
    }
}

我们不能调用requestSpecification.parameters的现有属性:

// no error!!!
console.log(requestSpecification.parameters.asdf);

我希望TypeScript知道id中只有nicknameparameters可用。 如何通过泛型解决?

1 个答案:

答案 0 :(得分:2)

您使用的字符串索引类型告诉TypeScript parameters可以具有字符串的任何属性。换句话说,'asdf'(以及任何其他字符串)被允许作为属性。不允许将数字作为属性。由于这是您声明的类型,因此TypeScript的行为符合预期,因为它允许'asdf'作为parameters的属性。

这是关于字符串索引类型的常见混淆,可能会导致您在TypeScript生涯中一遍又一遍地感到惊讶。

缩小类型的一种方法是to use a generic like this。在下面的示例中,我们说P扩展了字符串。结果,parameters仍然受限于字符串属性。实例化类型时,我们传入的是'id' | 'nickname'的字符串并集。这就告诉编译器确切地允许使用两个字符串。

type RequestSpecification<P extends string> = {
  type: "get" | "post";
  parameters: { [K in P]: ParameterSpecification }
};

type ParameterSpecification = {
  required: boolean;
}

const requestSpecification: RequestSpecification<'id' | 'nickname'> = {
    type: "get",
    parameters: {
        id: {
            required: true
        },
        nickname: {
            required: false
        }
    }
}

// This will now be a compiler error. Hooray! 
console.log(requestSpecification.parameters.asdf);