如何正确声明以下打字稿 interfaces/types?
How do I declare the following typescript interfaces/types correctly?
我正在使用 Typescript 构建 Apollo GraphQL 服务器,但无法理解在类型系统中处理事物的正确方法。尽管 GraphQL 和 Apollo 是代码的一部分,但我特别想知道 TypeScript 部分。我也无法理解接口与类型的作用以及每种接口的最佳实践(即,何时使用类型与接口,如何处理扩展等)。
我的大部分不明之处都在解析器中。我将在我要询问的相关部分旁边的代码中留下评论和问题。再次感谢您提供的任何帮助:
type BankingAccount = {
id: string;
type: string;
attributes: SpendingAccountAttributes | SavingsAccountAttributes
}
// I've realized this "SpendingAccountAttributes | SavingsAccountAttributes" is the wrong way
// to do what I'm trying to do. I essentially want to the tell the
// type system that this can be one or the other. As I understand it, the way it is written
// will create a Union, returning only the fields that are shared between both types, in this
// case what is essentially in the `BankingAttributes` type. Is that correct?
interface BankingAttributes = {
routingNumber: string;
accountNumber: string;
balance: number;
fundsAvailable: number;
}
// Is it better to remove the `SpendingAccountAttributes` and `SavingsAccountAttribute` specific
// types and just leave them as optional types on the `BankingAttributes`. I will
// in time be creating a resolver for the `SpendingAccount` and `SavingAccount` as standalone
// queries so it seems useful to have them. Not sure though
interface SpendingAccountAttributes extends BankingAttributes {
defaultPaymentCardId: string;
defaultPaymentCardLastFour: string;
accountFeatures: Record<string, unknown>;
}
interface SavingsAccountAttributes extends BankingAttributes {
interestRate: number;
interestRateYTD: number;
}
// Mixing types and interfaces seems messy. Which one should it be? And if "type", how can I
// extend the "BankingAttributes" to "SpendingAccountAttributes" to tell the type system that
// those should be a part of the SpendingAccount's attributes?
export default {
Query: {
bankingAccounts: async(_source: string, _args: [], { dataSources}: Record<string, any>) : Promise<[BankingAccount]> => {
// The following makes a restful API to an `accounts` api route where we pass in the type as an `includes`, i.e. `api/v2/accounts?types[]=spending&types[]=savings
const accounts = await.dataSources.api.getAccounts(['spending', 'savings'])
const response = accounts.data.map((acc: BankingAccount) => {
const { fundsAvailable, accountFeatures, ...other } = acc.attributes
return {
id: acc.id,
type: acc.type,
balanceAvailableForWithdrawal: fundsAvailable,
// accountFeatures fails the compilation with the following error:
// "accountFeatures does not exist on type 'SpendingAccountAttributes | SavingsAccountAttributes'
// What's the best way to handle this so that I can pull the accountFeatures
// for the spending account (which is the only type of account this attribute will be present for)?
accountFeatures,
...other
}
})
return response
}
}
}
我的经验法则是尽可能使用 interfaces
。基本上,只要处理具有已知键(值可以是复杂类型)的对象,就可以使用接口。所以你可以让 BankingAccount
成为 interface
.
您设置支出和储蓄账户以同时扩展共享界面的方式很棒!
当你有一个 BankingAccount
时,你知道它有消费或储蓄属性,但你不知道是哪个。
一个选项是使用 type guard.
检查它使用的是哪种类型
另一种选择是定义一个额外的 type
,它具有两种帐户类型的所有属性,但可选。
type CombinedAttributes = Partial<SpendingAccountAttributes> & Partial<SavingsAccountAttributes>
我个人会做的是定义您的 BankingAccount
,使其必须具有用于支出或储蓄的完整属性,但其他类型的属性是可选的。这意味着您可以毫无错误地访问这些属性,但它们可能是 undefined
.
interface BankingAccount = {
id: string;
type: string;
attributes: (SpendingAccountAttributes | SavingsAccountAttributes) & CombinedAttributes
}
输入所有这些后...我意识到 BankingAccount
有一个 type
。那种类型是“支出”还是“储蓄”?在那种情况下,我们还想在 type
和 attributes
之间绘制一个 link。
此 BankingAccount
类型定义应允许您访问任一类型的属性,同时还允许您仅通过检查 type
的值来将帐户缩小为储蓄或支出。由于这里的联合,这必须是 type
而不是 interface
,但这并不是真正重要的。
type BankingAccount = {
id: string;
attributes: CombinedAttributes;
} & ({
type: "savings";
attributes: SavingsAccountAtrributes;
} | {
type: "spending";
attributes: SpendingAccountAttributes;
})
function myFunc( account: BankingAccount ) {
// interestRate might be undefined because we haven't narrowed the type
const interestRate: number | undefined = account.attributes.interestRate;
if ( account.type === "savings" ) {
// interestRate is known to be a number on a savings account
const rate: number = account.attributes.interestRate
}
}
我正在使用 Typescript 构建 Apollo GraphQL 服务器,但无法理解在类型系统中处理事物的正确方法。尽管 GraphQL 和 Apollo 是代码的一部分,但我特别想知道 TypeScript 部分。我也无法理解接口与类型的作用以及每种接口的最佳实践(即,何时使用类型与接口,如何处理扩展等)。
我的大部分不明之处都在解析器中。我将在我要询问的相关部分旁边的代码中留下评论和问题。再次感谢您提供的任何帮助:
type BankingAccount = {
id: string;
type: string;
attributes: SpendingAccountAttributes | SavingsAccountAttributes
}
// I've realized this "SpendingAccountAttributes | SavingsAccountAttributes" is the wrong way
// to do what I'm trying to do. I essentially want to the tell the
// type system that this can be one or the other. As I understand it, the way it is written
// will create a Union, returning only the fields that are shared between both types, in this
// case what is essentially in the `BankingAttributes` type. Is that correct?
interface BankingAttributes = {
routingNumber: string;
accountNumber: string;
balance: number;
fundsAvailable: number;
}
// Is it better to remove the `SpendingAccountAttributes` and `SavingsAccountAttribute` specific
// types and just leave them as optional types on the `BankingAttributes`. I will
// in time be creating a resolver for the `SpendingAccount` and `SavingAccount` as standalone
// queries so it seems useful to have them. Not sure though
interface SpendingAccountAttributes extends BankingAttributes {
defaultPaymentCardId: string;
defaultPaymentCardLastFour: string;
accountFeatures: Record<string, unknown>;
}
interface SavingsAccountAttributes extends BankingAttributes {
interestRate: number;
interestRateYTD: number;
}
// Mixing types and interfaces seems messy. Which one should it be? And if "type", how can I
// extend the "BankingAttributes" to "SpendingAccountAttributes" to tell the type system that
// those should be a part of the SpendingAccount's attributes?
export default {
Query: {
bankingAccounts: async(_source: string, _args: [], { dataSources}: Record<string, any>) : Promise<[BankingAccount]> => {
// The following makes a restful API to an `accounts` api route where we pass in the type as an `includes`, i.e. `api/v2/accounts?types[]=spending&types[]=savings
const accounts = await.dataSources.api.getAccounts(['spending', 'savings'])
const response = accounts.data.map((acc: BankingAccount) => {
const { fundsAvailable, accountFeatures, ...other } = acc.attributes
return {
id: acc.id,
type: acc.type,
balanceAvailableForWithdrawal: fundsAvailable,
// accountFeatures fails the compilation with the following error:
// "accountFeatures does not exist on type 'SpendingAccountAttributes | SavingsAccountAttributes'
// What's the best way to handle this so that I can pull the accountFeatures
// for the spending account (which is the only type of account this attribute will be present for)?
accountFeatures,
...other
}
})
return response
}
}
}
我的经验法则是尽可能使用 interfaces
。基本上,只要处理具有已知键(值可以是复杂类型)的对象,就可以使用接口。所以你可以让 BankingAccount
成为 interface
.
您设置支出和储蓄账户以同时扩展共享界面的方式很棒!
当你有一个 BankingAccount
时,你知道它有消费或储蓄属性,但你不知道是哪个。
一个选项是使用 type guard.
检查它使用的是哪种类型另一种选择是定义一个额外的 type
,它具有两种帐户类型的所有属性,但可选。
type CombinedAttributes = Partial<SpendingAccountAttributes> & Partial<SavingsAccountAttributes>
我个人会做的是定义您的 BankingAccount
,使其必须具有用于支出或储蓄的完整属性,但其他类型的属性是可选的。这意味着您可以毫无错误地访问这些属性,但它们可能是 undefined
.
interface BankingAccount = {
id: string;
type: string;
attributes: (SpendingAccountAttributes | SavingsAccountAttributes) & CombinedAttributes
}
输入所有这些后...我意识到 BankingAccount
有一个 type
。那种类型是“支出”还是“储蓄”?在那种情况下,我们还想在 type
和 attributes
之间绘制一个 link。
此 BankingAccount
类型定义应允许您访问任一类型的属性,同时还允许您仅通过检查 type
的值来将帐户缩小为储蓄或支出。由于这里的联合,这必须是 type
而不是 interface
,但这并不是真正重要的。
type BankingAccount = {
id: string;
attributes: CombinedAttributes;
} & ({
type: "savings";
attributes: SavingsAccountAtrributes;
} | {
type: "spending";
attributes: SpendingAccountAttributes;
})
function myFunc( account: BankingAccount ) {
// interestRate might be undefined because we haven't narrowed the type
const interestRate: number | undefined = account.attributes.interestRate;
if ( account.type === "savings" ) {
// interestRate is known to be a number on a savings account
const rate: number = account.attributes.interestRate
}
}