&quot'功能与许多冗余功能与打字稿的字符串枚举

发布于 2025-01-29 06:53:35 字数 3612 浏览 2 评论 0 原文

我在以下结构中有一个对象:

const geo = {
    europe: {
      germany: ['berlin', 'hamburg', 'cologne'],
      france: ['toulouse', 'paris', 'limoges'],
      italy: ['rome', 'venice', 'genoa'],
    },
    asia: {
      india: ['mumbai', 'rajkot', 'pune'],
      china: ['shenzhen', 'beijing', 'shanghai'],
      nepal: ['kohalpur', 'ghorahi', 'hetauda'],
    },
  };

我想编写一个简单的函数,该功能随机选择了一个城市和乡村。尽管在我看来,直接的实现似乎是

const randomElement = (arr) => arr[Math.floor(Math.random() * arr.length)];
const pickCity = (continent, country) => randomElement(geo[continent][country])

// calling `pickCity()`
pickCity("europe", "france") // => we get one city at random, as requested

我想知道这种实现是否属于“ noreflow noreferrer”>“刻板地打字“ 函数,据说这是一种优化的实践。

另一方面,如果我试图避免使用这些字符串参数,那么替代方案将更详细,而不是尊重干燥原则:

const pickCityGermany = () => randomElement(geo.europe.germany)
const pickCityFrance = () => randomElement(geo.europe.france)
const pickCityItaly = () => randomElement(geo.europe.italy)
const pickCityIndia = () => randomElement(geo.asia.india)
const pickCityChina = () => randomElement(geo.asia.china)
const pickCityNepal = () => randomElement(geo.asia.nepal)

IS pickcity()确实是“意上地键入”的,或者我正在误解概念?


更新


以下 @bergie的答案下面的更新,我已经意识到 typescript 's enum 可能是键入 pickcity()的参数的正确解决方案。因此,我对此表示了存根,但是请参阅下面,这仍然不是一个完整的解决方案。

我首先创建两个枚举 s,一个用于大陆,一个用于国家:

// typescript
enum Continent {
    Europe = "europe",
    Asia = "asia"
}

enum Country {
    Germany = "germany",
    France = "france",
    Italy = "italy",
    India = "india",
    China = "china",
    Nepal = "nepal"
}

但是,正如@bergi所写,它们彼此依赖,因为 geo 数据是构造的。 。因此,我现在解决的唯一方法是键入任何,这肯定不是我想要的解决方案:

type Geo = Record<Continent, any> // <~-~-~-~-~ `any` is :(

const geo: Geo = {
    europe: {
      germany: ['berlin', 'hamburg', 'cologne'],
      france: ['toulouse', 'paris', 'limoges'],
      italy: ['rome', 'venice', 'genoa'],
    },
    asia: {
      india: ['mumbai', 'rajkot', 'pune'],
      china: ['shenzhen', 'beijing', 'shanghai'],
      nepal: ['kohalpur', 'ghorahi', 'hetauda'],
    },
  };

const randomElement = (arr) => arr[Math.floor(Math.random() * arr.length)];
const pickCity2 = (continent: Continent, country: Country) => randomElement(geo[continent][country])

// calling pickCity2()
console.log(pickCity2(Continent.Europe, Country.Germany)) // => one German city at random

为什么我们不能简单地通过更改 geo <

@bergi问这个,因为它似乎是不必要的并发症。 如果我们有以下地geosimpler ,生活会更容易:

type GeoSimpler = Record<Country, string[]>;

const geoSimpler: GeoSimpler = {
  germany: ['berlin', 'hamburg', 'cologne'],
  france: ['toulouse', 'paris', 'limoges'],
  italy: ['rome', 'venice', 'genoa'],
  india: ['mumbai', 'rajkot', 'pune'],
  china: ['shenzhen', 'beijing', 'shanghai'],
  nepal: ['kohalpur', 'ghorahi', 'hetauda'],
};
const pickCity3 = (country: Country) => randomElement(geoSimpler[country])
console.log(pickCity3(Country.France)) // 
              

I have an object in the following structure:

const geo = {
    europe: {
      germany: ['berlin', 'hamburg', 'cologne'],
      france: ['toulouse', 'paris', 'limoges'],
      italy: ['rome', 'venice', 'genoa'],
    },
    asia: {
      india: ['mumbai', 'rajkot', 'pune'],
      china: ['shenzhen', 'beijing', 'shanghai'],
      nepal: ['kohalpur', 'ghorahi', 'hetauda'],
    },
  };

And I want to write a simple function that randomly selects a city, given continent and country. Although it seems to me that a straightforward implementation would be

const randomElement = (arr) => arr[Math.floor(Math.random() * arr.length)];
const pickCity = (continent, country) => randomElement(geo[continent][country])

// calling `pickCity()`
pickCity("europe", "france") // => we get one city at random, as requested

I nevertheless wonder whether such implementation is falling under the category of a "stringly typed" function, which is said to be a sub-optimal practice.

On the other hand, if I attempt to avoid those string parameters, then the alternative would be more verbose and not respecting the DRY principle:

const pickCityGermany = () => randomElement(geo.europe.germany)
const pickCityFrance = () => randomElement(geo.europe.france)
const pickCityItaly = () => randomElement(geo.europe.italy)
const pickCityIndia = () => randomElement(geo.asia.india)
const pickCityChina = () => randomElement(geo.asia.china)
const pickCityNepal = () => randomElement(geo.asia.nepal)

Is pickCity() indeed "stringly typed", or am I misinterpreting the concept?


UPDATE


Following @Bergie's answer below, I've realized that TypeScript's enum is likely a proper solution to typing pickCity()'s parameters. So I took a stub at this, but see below, this is still not quite a complete solution.

I started with creating two enums, one for continent and one for country:

// typescript
enum Continent {
    Europe = "europe",
    Asia = "asia"
}

enum Country {
    Germany = "germany",
    France = "france",
    Italy = "italy",
    India = "india",
    China = "china",
    Nepal = "nepal"
}

However, as @Bergi wrote, they are dependent on each other because the way geo data is structured. So the only way I could solve it right now is by typing any, which is certainly not a solution I want:

type Geo = Record<Continent, any> // <~-~-~-~-~ `any` is :(

const geo: Geo = {
    europe: {
      germany: ['berlin', 'hamburg', 'cologne'],
      france: ['toulouse', 'paris', 'limoges'],
      italy: ['rome', 'venice', 'genoa'],
    },
    asia: {
      india: ['mumbai', 'rajkot', 'pune'],
      china: ['shenzhen', 'beijing', 'shanghai'],
      nepal: ['kohalpur', 'ghorahi', 'hetauda'],
    },
  };

const randomElement = (arr) => arr[Math.floor(Math.random() * arr.length)];
const pickCity2 = (continent: Continent, country: Country) => randomElement(geo[continent][country])

// calling pickCity2()
console.log(pickCity2(Continent.Europe, Country.Germany)) // => one German city at random

why can't we simply untangle the dependency by changing geo?

@Bergi asked this, because it's seems like an unneeded complication. So had we have the following geoSimpler instead, life would have been easier:

type GeoSimpler = Record<Country, string[]>;

const geoSimpler: GeoSimpler = {
  germany: ['berlin', 'hamburg', 'cologne'],
  france: ['toulouse', 'paris', 'limoges'],
  italy: ['rome', 'venice', 'genoa'],
  india: ['mumbai', 'rajkot', 'pune'],
  china: ['shenzhen', 'beijing', 'shanghai'],
  nepal: ['kohalpur', 'ghorahi', 'hetauda'],
};
const pickCity3 = (country: Country) => randomElement(geoSimpler[country])
console.log(pickCity3(Country.France)) // ????

However!

I still want to account for data structures that could not be simplified. For example, consider geoTwinCitiesAttractions:

const geoTwinCitiesAttractions = {
  us: {
    cairo: ['U.S. Custom House', 'Cairo Public LIbrary', 'Magnolia Manor'],
    memphis: ['Graceland', 'National Civil Rights Museum', 'Beale Street'],
    saintPetersburg: ['The Dali Museum', 'Sunken Gardens', 'Chihuly Collection'],
    moscow: [],
    athens: ['Pleasant Hill Vineyards', 'Little Fish Brewing', 'Athens Farmers Market'],
  },
  greece: {
    athens: ['Acropolis of Athens', 'Parthenon', 'Mount Lycabettus'],
  },
  france: {
    paris: ['Eiffel Tower', 'Louvre Museum', 'Arc de Triomphe'],
  },
  russia: {
    saintPetersburg: ['State Hermitage Museum', 'Savior on the Spilled Blood', 'Winter Palace',
    ],
    moscow: ['Red Square', 'Bolshoi Theatre', 'Cathedral of Christ the Saviour'],
  },
  egypt: {
    cairo: ['Giza Necropolis', 'Mosque of Muhammad Ali', 'Salah Al-Din Al-Ayoubi Castle'],
    memphis: ['foo', 'bar', 'baz'],
  },
};

Here we must specify both country and city, because there are same-name cities in different countries. So how could we use enum to avoid the "stringly typed" pickAttraction():

const pickAttraction = (country: string, city: string) => randomElement(geoTwinCitiesAttractions[country][city])

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

北方。的韩爷 2025-02-05 06:53:35

是的,如果您确实考虑将任意字符串传递给,则 pickcity 是“觉得键入” pickcity('非洲','sudan')因当前数据结构而失败。更糟糕的是, pickcity('sun','Earth') pickcity('Major:Scholz','人口:&GT; 10000') pickcity( '','ivqu ioqwe h')根本没有任何意义。

但是,如果您不将任意字符串传递给它,那就可以了,如果仅将非洲人名称传递给第一个参数,而将状态名称仅传递给第一个参数。使用枚举类型更适合键入这些参数。但是,由于您特别询问JavaScript,因此没有枚举类型(根本没有类型的声明),并且在运行时使用字符串是最可行的解决方案,如果您需要选项之间的动态选择。但是您的心理模型(以及注释和文档)必须将其视为枚举,或将其视为一组允许的字符串。

(另一个与“诱人打字”无关的问题是,这些参数彼此依赖。您是否需要大陆?)

Yes, pickCity is "stringly typed" if you really consider passing arbitrary strings to it. pickCity('africa', 'sudan') fails with your current data structure. And worse, pickCity('sun', 'earth') or pickCity('major:Scholz', 'population:>10000') or pickCity('','ivqu ioqwe h') just don't make sense at all.

But it's fine if you don't pass arbitrary strings to it, if you pass only continent names to the first parameter and only state names to the first parameter. Using enum types would be more appropriate to type these parameters. However, since you're asking about JavaScript specifically, there are no enum types (nor type declarations at all), and at runtime using strings is the most viable solution if you need dynamic choice between the options. But your mental model (and also annotations and documentation) must treat these as enums, or as a set of allowed strings.

(A further problem, unrelated to the "stringly typed", is that the parameters are dependent on each other. pickCity('america', 'germany') doesn't work since Germany is not in America. Do you need the continent at all?)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文