我在以下结构中有一个对象:
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 enum
s, 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])
发布评论
评论(1)
是的,如果您确实考虑将任意字符串传递给,则
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')
orpickCity('major:Scholz', 'population:>10000')
orpickCity('','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?)